kotlin高阶函数

这篇具有很好参考价值的文章主要介绍了kotlin高阶函数。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

kotlin高阶函数

函数式API:一个函数的入参数为Lambda表达式的函数就是函数式api
例子:

public inline fun <T> Iterable<T>.filter(predicate: (T) -> Boolean): List<T> {
    return filterTo(ArrayList<T>(), predicate)
}

上面这段函数: 首先这个函数是一个泛型函数
泛型函数的定义:就是我们在写完一个函数后,只知道一个总的类型,而这个总的类型下有很多继承了这个总类型的类,在返回时我们不知道这个函数具体返回哪个子类,这时我们就可以在这个函数前面加一个泛型,泛型中放这些子类的基类,上面的filter方法也可以看作 Iterable的扩展方法

例子:

// A为基类
public class A{

}
public class  B extends A{

}

public class  C extends A{

}	

// 我们不知道下面这个方法具体返回A,B,C到底哪一个类型时,因为类B和C继承自A,这时我们就可以将这个返回类型指定为基类型A

public static <A> test(){

}

下面我写kotlin的写法

class ReportV2Controller : Controller(){
}

和上面java一样有多个类继承自Controller()这个类,kotlin中继承都是以函数的形式,因为这样写直观的表现了自类具体会调用基类的哪个构造函数

	public inline fun <T> Iterable<T>.filter(predicate: (T) -> Boolean): List<T> {
    return filterTo(ArrayList<T>(), predicate)
}		

上面的filter方法是迭代集合Iterable,但是在kotlin中这个Iterable是一个被inline修饰的内联函数

内联函数的出现的原因:
1.首先在kotlin中有高阶函数的原因,因为高阶函数的传参可以是一个函数,而且出参也可以为一个函数

// 下面这段代码原作者为csdn: Mr YiRan
fun num1AndNum2(num1:Int,num2:Int,operator:(Int,Int)->Int):Int{
    val result=operator(num1,num2)
    return result
}

上面这段代码高阶函数num1AndNum2入参为两个Int类型与一个函数类型operator,这个operator又接收两个int类型的参数,这个operator返回类型为int,operator返回的类型int同时也是num1AndNum2高阶函数的返回类型,同时num1AndNum2函数的入参数,也可以当作函数operator的入参

fun num1AndNum2(num1:Int,num2:Int,operator:(Int,Int)->Int):Int{
    val result=operator(num1,num2)
    return result
}

// 定义两个具体的运算函数
fun plus(num1: Int,num2: Int):Int{
return num1+num2   
}
fun minus(num1: Int,num2: Int):Int{
    return num1-num2
}


fun main(){
    val num1=100
    val num2=80
    // 这是一个函数引用方式的写法,表示将plus()和minus()函数作为参数传递给num1AndNum2()函数
    val result1= num1AndNum2(num1,num2,::plus)
    val result2 = num1AndNum2(num1, num2, ::minus)
    println("result1 is $result1")
    println("result2 is $result2")
}



fun main(){
val num1=100
val num2=80
// 上面的函数引用写法的调用可以改为Lambda表达式的方式来调用高阶函数
val result1=num1AndNum2(num1,num2){ n1,n2 ->
n1+n2
}
val result2=num1AndNum2(num1,num2){ n1,n2 ->
n1-n2
}
println("result1 is $result1")
println("result2 is $result2")
}




Lambda表达式的优点与缺点

优点 :
如果一个抽象类或者接口中只有一个方法,我们又不想实例化这个类的对象就想调用这个方法,而且也不想将这个类中的方法标记为静态的方法,就可以用匿名内部类的方式类写,但是这样写代码很不美观,所以就简化成Lambda表达式的方式来写

缺点:
Lambda表达式的方式,系统会默认实例化一个匿名内部类的方式,就会造成额外的内存开销与cpu性能损耗
因为就算高阶函数中的传参写成Lambda表达式的方式,其内部运行顺序也为先实例化最外层类的对象,然后通过对象去引用当前这个对象的方法,将当前对象的方法存入方法区进行压栈然后再实例化一个Lambda表达式的匿名内部类,再通过这个对象去引用这个匿名内部类中的方法,再进行压榨

kotlin是如何解决这种因为传入Lambda表达式写法的性能开销的

内联函数

在定义高阶函数时加上inline关键字,就表示此高阶函数是一个内联函数

inline fun num1AndNum2(num1:Int,num2:Int,operation:(Int,Int)->Int):Int{
val result=operation(num1,num2)
return result
}

如何消除 Lambda表达式额外开销
简单来说就是:在调用传参为 Lambda表达式的高阶函数时,将 Lambda表达式的函数体直接拷贝在调用参数为 Lambda表达式的高阶函数后方
示例
kotlin高阶函数,kotlin,开发语言,java
kotlin高阶函数,kotlin,开发语言,java
1.首先将 Lambda表达式的入参拷贝到函数类型参数的参数入参列表中
2.其次再将内联函数的方法体拷贝到调用该高阶函数的地方
3.总结为:将实例化匿名内部类调用方法压栈改为方法入参数拷贝和方法体拷贝的过程

下面给一个返回值是一个函数的高阶函数
示例

/**

  • 定义一个返回值是函数的高阶函数
  • @param name 入场
  • @return 返回一个函数或者lambda
    */
    fun highFunction2(name:String):(Int) -> Int{
    if (name == “A”){
    // 这里返回一个函数引用,意思调用returnFun函数
    // 上面的highFunction2函数入参为两个一个String类型的name,还有一个入参是一个参数为一个int类型的Lambda表达式
    // 如果想调用returnFun函数那highFunction2函数传入的Lambda表达式入参类,数量与顺序必须完全与函数returnFun一致
    return ::returnFun
    }
    //返回lambda
    return {a -> a + 10}
    }

/**

  • 作为高阶函数的返回函数
    */
    fun returnFun(a:Int):Int{
    return a * 100
    }
//使用高阶函数
val res = highFunction2("A")
println(res(20)) //打印2000
val res2 = highFunction2("B")
println(res2(20)) //打印30

扩展函数

fun main() {
    open class Shape

    class Rectangle: Shape()

    fun Shape.getName() = "Shape"

    fun Rectangle.getName() = "Rectangle"

    fun printClassName(s: Shape) {
        println(s.getName())
    }    

    printClassName(Rectangle())

上面函数的最终返回结果是打印"Shape",因为虽然printClassName的函数的传入参数是Rectangle()函数,但是最终会调用Shape类的getName()函数,这是因为最终返回的类型取决于printClassName函数传入参数s的类型

成员函数与扩展函数

如果一个类定义有一个成员函数与一个扩展函数,而这两个函数又有相同的接收者类型、 相同的名字,并且都适用给定的参数,这种情况总是取成员函数
示例

fun main() {
    class Example {
        fun printFunctionType() { println("Class method") }
    }

    fun Example.printFunctionType() { println("Extension function") }

    Example().printFunctionType()
}

这里最后会输出:“Class method”,因为是以类的成员函数为准的,优先级为先成员函数
如果是扩展类型的重载,那么以传入参数为准
由于静态调用扩展方法是在编译时执行,因此,如果父类和子类都扩展了同名的一个扩展方法,引用类型均为父类的情况下,会调用父类的扩展方法
示例

fun main() {
    class Example {
        fun printFunctionType() { println("Class method") }
    }

    fun Example.printFunctionType(i: Int) { println("Extension function") }

    Example().printFunctionType(1)
}

最后输出结果为:Extension function

top-level函数

不依赖于任何类的静态函数,经Kotlin编译成Java文件后,成为:<静态函数所在的文件名> + "Kt"的Java类的静态成员函数。如果原kotlin文件的文件名首字母为小写时,转换成大写。
示例

/** joinsample.kt */
package com.example.kotlin

import java.lang.StringBuilder

fun <T> joinToString(
    collection: Collection<T>,
    separator: String = ", ",
    prefix: String = "",
    postfix: String = ""
): String {
    val result = StringBuilder(prefix)
    for ((index, element) in collection.withIndex()) {
        if (index > 0) {
            result.append(separator)
        }

        result.append(element)
    }
    result.append(postfix)

    return result.toString()
}
import com.example.kotlin.JoinsampleKt;
import java.util.Arrays;
import java.util.List;

public class JavaSample {
    public static void main(String[] args) {
        List<String> list = Arrays.asList("hello", "world");
        JoinsampleKt.joinToString(list, ", ", "", "");
    }
}

扩展属性

由于扩展没有实际的将成员插入类中,因此对扩展属性来说幕后字段是无效的。这就是为什么扩展属性不能有初始化器。他们的行为只能由显式提供的 getters/setters 定义。

扩展声明为成员

就是说有两个类,可以在其中一个类中为另一个类声明一个扩展函数,被声明的扩展函数,此时是当前类的成员函数

示例

class Host(val hostname: String) {
    fun printHostname() { print(hostname) }
}

class Connection(val host: Host, val port: Int) {
     fun printPort() { print(port) }
	
 	// 这里可以为Host类定义扩展函数,是因为类Connection的主构造函数中将Host类传入进来
     fun Host.printConnectionString() {
         printHostname()   // 调用 Host.printHostname()
         print(":")
         printPort()   // 调用 Connection.printPort()
     }

     fun connect() {
         /*……*/
         host.printConnectionString()   // 调用扩展函数
     }
}

fun main() {
	// 这里直接使用类Connection的主构造函数传入Host类然后调用Host的主构造函数,然后调用Host类的printHostname()函数
	// 被定义的扩展函数必须在定义该扩展函数的类中使用
	// 意思就是说Host的printConnectionString()函数必须在类Connection中使用
	//  Connection(Host("kotl.in"),443).printConnectionString()
    Connection(Host("kotl.in"), 443).connect()
    //Host("kotl.in").printConnectionString(443)  // 错误,该扩展函数在 Connection 外不可用
}
class Connection {
	// 这里给Host类定义一个扩展函数
	// 在这个扩展函数中调用toString()
	// 因为在kotlin中所有的类都继承自Any类,而toString方法是属于Any类的
	// 如果想调用Connection类的toString()方法需要向下面那种写法
	// this@Connection.toString()
    fun Host.getConnectionString() {
        toString()         // 调用 Host.toString()
        this@Connection.toString()  // 调用 Connection.toString()
    }
}

泛型中的in和out

这两个泛型的定义为逆变与携变
示例

// 逆变
interface Consumer<in T> {
    fun consume(item: T)
}

简单来说就是在接口中in标记的泛型类型T,要作为函数consume的入参类型

// 携变
interface Production<out T> {
    fun produce(): T
}

简单来说就是接口泛型中out标记的T要做为函数produce返回的类型文章来源地址https://www.toymoban.com/news/detail-608587.html

到了这里,关于kotlin高阶函数的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Kotlin Lambda和高阶函数

    本文链接: 1、lambda的由来 单词\\\"lambda\\\"源于希腊字母λ(小写lambda) \\\"Lambda\\\"一词最早起源于数学中的λ演算(lambda calculus),它是一种函数定义和函数应用的形式系统,由逻辑学家阿隆佐·邱奇(Alonzo Church)在20世纪30年代发明。 邱奇使用λ作为演算中的一个操作符,用于表示匿

    2024年02月12日
    浏览(45)
  • Kotlin(十六) 高阶函数的简单应用

    高阶函数非常适用于简化各种API的调用,一些API的原有用法在使用高阶函数简化之后,不管是在易用性还是可读性方面,都可能会有很大的提升。 所以我们可以通过高阶函数来使一些API变得更简单更易读。在我们APP存储数据时,通常会用到SharedPreferences 这个API,那么我们现在

    2024年02月04日
    浏览(47)
  • 【Kotlin】探索回调机制:函数类型、高阶函数和接口

    当在 Kotlin 中开发应用程序时,经常会遇到需要使用回调机制的情况。回调是一种常见的编程模式,用于在异步操作完成后通知调用方,并处理相应的结果或事件。在 Kotlin 中,有几种不同的方法可以实现回调,包括使用函数类型、高阶函数和接口。每种方法都有其优点和适用

    2024年02月02日
    浏览(47)
  • 【Kotlin】函数式编程 ① ( 函数式编程简介 | 高阶函数 | 函数类别 | Transform 变换函数 | 过滤函数 | 合并函数 | map 变换函数 | flatMap 变换函数 )

    编程范式 指的是 使用某种编程语言的 编程套路 或 编程习惯 ; 使用 Java 等高级语言进行的编程 , 编程范式 一般都是 面向对象编程 ; 与 面向对象编程 同等级的另外一种 编程范式 是 函数式编程 , 函数式编程 不依赖于 指定的语言 , 所有的编程语言都可以使用 函数式编程范式

    2024年01月18日
    浏览(47)
  • Android 安卓开发语言kotlin与Java该如何选择

            如今在Android开发中,应用层开发语言主要是Java和Kotlin,Kotlin是后来加入的,主导的语言还是Java。kotlin的加入仿佛让会kotlin语言的开发者更屌一些,其实不然。         有人说kotlin的引入是解决开发者复杂的逻辑,并且对空指针控制的比较友好,但是我们在开

    2024年02月11日
    浏览(66)
  • 高阶C语言|字符函数和字符串函数--函数的模拟实现

    C语言中对字符和字符串的处理很是频繁,但是C语言本身是没有字符串类型的,字符串通常放在 常量字符串 中或者 字符数组 中。 字符串常量 适用于那些对它不做修改的字符串函数 size_t代表strlen函数返回的是一个无符号整形,str指向的是字符串,接收字符串的地址 字符串已

    2024年02月16日
    浏览(70)
  • 【第三阶段】kotlin语言的内置函数takeif

    执行结果

    2024年02月11日
    浏览(42)
  • kotlin基础--快速上手kotlin语言开发

    1.1 变量 var表示可变变量,val表示不可变变量,注意并不是常量。变量名写在前面,类型写在后面,编译器如果能推断出你的类型,那么类型是不用声明的 。 编译器自动推断类型。 空安全类型编译器报错 如果还是想给赋初始化值的话 注意:String和String?是两个完全不同的类

    2024年02月15日
    浏览(54)
  • 【Kotlin】Kotlin 与 Java 互操作 ① ( 变量可空性 | Kotlin 类型映射 | Kotlin 访问私有属性 | Java 调用 Kotlin 函数 )

    在 Java 语言 中 , 任何 引用类型变量 都可以为 空 null ; Java 中 八种 基本数据类型 变量 的 默认值 为 0 或 false ; 但是在 Kotlin 语言 中 , 所有的 变量 都是引用类型变量 , 没有基本数据类型 , 默认情况下 所有的变量 都为 非空类型 ; 下面分别定义一个 Java 类 和 Kotlin 脚本 , 在 K

    2024年02月02日
    浏览(64)
  • 【Kotlin】DSL 领域特定语言 ( apply 标准库函数分析 | 普通匿名函数 | 扩展匿名函数 | 泛型扩展匿名函数 )

    本章总结 : 读懂 apply 标准库函数 核心是其 block: T.() - Unit 参数 , 这是 泛型扩展匿名函数 ; 泛型扩展匿名函数 T.() - Unit 演变路径 : 普通匿名函数 : () - Unit , 这个函数 参数 和 返回值 都为空 ; 扩展匿名函数 : String.() - Unit , 这个函数 是 为 具体的 String 类型定义的扩展函数 ; 泛型

    2023年04月09日
    浏览(39)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包