Kotlin再学習シリーズ-高階関数

一緒に書く習慣を身につけましょう!「ナゲッツデイリーニュープラン・4月アップデートチャレンジ」に参加した初日です。クリックしてイベントの詳細をご覧ください。

序文

これは、kotlin再学習シリーズの最初の記事です。高階関数については、この記事を辛抱強く読んだ後に学習します。

  • kotlin高階関数の本質を理解する
  • kotlinのラムダ式を理解する
  • kotlinのラムダとjavaラムダの違いを理解する

kotlin再学習シリーズの最初の記事として高階関数を使用するのはなぜですか?はい、それは重要だからです!高階関数をマスターしないと、kotlin拡張機能、コルーチン、DSLなど、ほとんどすべてが高階関数に密接に関連しているため、多くのkotlin構文は不快に見えるか、完全に理解できないように見えます。高階関数はjavaを書くのと何ら変わりはないと言った

関数型

Javaには関数型がありますか?いいえ、javaデータ型には、int、longなどの基本データ型と、Stringやarrayなどの参照型のみが含まれます。

では、なぜkotlinは別の関数型を導入するのですか?効果は何ですか?

ここにそのようなロジックがあるとします。関数を自分で定義しますが、この関数には、呼び出し元が定義したいロジックの一部があります。つまり、呼び出し元はコードロジックの一部を渡します。これをJavaで実装しますか?

はい、クリックイベントをリッスンする方法などのインターフェイスを使用して、インターフェイスをSetOnClickListener受け取ります。OnClickListenerインターフェイスを匿名で実装して、コードロジックの一部を過去に渡します。

Javaは関数型をサポートしていないため、この関数をインターフェースでラップすることしかできないため、これは国を曲線から救う方法です。通常、IDEのオートコンプリート関数があるかどうかは問題ではありませんが、順番にこれを実現するために、私たちは非常に一般的なロジックです。毎回インターフェイスを定義し、それを実装することです。非常に多くの冗長なコードを見るのは少し苦痛です。

そのため、この種の問題を解決するために関数型がkotlinに導入され、関数を別の関数のパラメーターまたは戻り値として直接使用することもできます。

関数型の表現

異なる関数間での主な違いは入力パラメーターと戻り値の違いであるため、関数型の式は次のようになります。

(パラメータ)->戻り値

矢印の左側の括弧は入力パラメーターのタイプ情報を示し(「、」で区切られた複数のパラメーターがあります)、矢印の右側は戻り値のタイプ情報です。栗を参照してください。

// 表示的函数有两个Int类型的参数,返回值也是Int
(IntInt)->Int

// 函数没有入参,返回值是空的
()->Unit
复制代码

高階関数

定义:如果一个函数的参数或者返回值是函数类型,那么这个函数就称之为高阶函数

例如

// 定义一个函数funA,接收一个函数类型的参数
fun funA(funParam: (Int) -> Int) {
    funParam(1)
}

// 定义一个函数funB,该函数入参为一个Int,返回值为一个Int
fun funB(param: Int): Int {
    return param+1
}

// 调用
funA(::funB)

复制代码

如上代码,函数funA就是一个高阶函数,因为他的入参包含了一个函数类型

又定义了一个函数funB,入参和返回值正好满足funA接收的函数类型,所以就可以把funB这个函数传递给funA

注意我们在调用的时候,不是直接把funB这个名字标识符传给funA,而是在前面加了一个::,那这个“::”是干什么的呢?

这个双冒号的写法叫做函数引用,也就是说::funB这个是指向函数funB的,为什么又需要整这些花里胡哨的,因为函数他毕竟本质还是一个函数,函数之所以可以作为参数传递给另外一个函数,他的本质是kotlin可以把函数包装为一个对象,也就是说::funB是一个函数类型的对象

不过实际开发中我目前很少用这种方式,大多数都是用匿名函数和lambda,这个才是重点,下面马上会讲到

匿名函数

没有名字的函数就叫匿名函数,还是上面那个demo,我们完全可以不单独定义一个函数funB,而是直接用匿名函数


funA(fun(param:Int):Int{
    return param+1
})

// 或者可以直接把匿名函数赋值给一个变量
val d = fun(param: Int): Int {
    return param+1
}
funA(d)
复制代码

我们看到这里匿名函数可以直接传递给函数,也可以直接赋值给变量,所以它跟上面的::funB就是等价的,也就是说匿名函数实质上是一个函数类型的对象

lambda

上面使用匿名函数的方式看着还是有点别扭,总感觉很臃肿,而kotlin的宗旨就是简洁,所以就有了lambda,像下面这样

funA({param:Int ->
    param+1
})
复制代码

lambda在匿名函数的基础上又省略了一些代码,包括省略了fun关键字,把参数也直接放在了大括号里面,这样看起来简洁了不少,不过lambda的简洁还不远不如此,下面会一步步简化

先看看lambda的标准格式

{参数->函数体}

lambda的内容都在一个大括号里面,用箭头分割成两部分,箭头左边是参数,箭头右边是函数体,如果没有参数,箭头以及左边的就直接省略不写

// 没有参数直接省略箭头和左边的内容
val lambda1 = { println("无参数的lambda") }


val lambda2 = { param: Int ->
    println("带参数的lambda$param")
}

// lambda的函数体的最后一行的结果表示的就是返回值
val lambda3 = { param: Int ->
    println("带参数的lambda$param")
    param
}
复制代码

上面的栗子展示了有参数和没有参数的lambda情况,另外值得注意的是lambda的最后一行的执行结果就是这个函数的返回值,例如上述lambda2的最后一行为println("带参数的lambda$param"),所以他的返回值类型为Unit,lambda3的最后一行是param,所以他的返回值就是这个param,也就是Int类型的

下面继续看看lambda表达式还能怎么简写

1.如果lambda是函数的最后一个参数,那么就可以把lambda写在括号外面

funA(){ param: Int ->
    param + 1
}
复制代码

2.如果函数只有lambda一个参数,括号里面就是空的,写了有何用,省略

funA{ param: Int ->
    param + 1
}
复制代码

3.kotlin支持类型推导,所以lambda中的参数类型也能省略

funA(){ param ->
    param + 1
}
复制代码

4.如果lambda只有一个参数,也可以省略

funA {
    it + 1
}
复制代码

如果lambda只有一个参数的时候,会默认提供一个名为it的参数来表示这个入参,在lambda内可以直接使用,不用声明

为什么越来越任性,啥都可以省略,那是因为kotlin坚持只要能推断出来的地方,能不写的代码就不写,例如这个地方的参数为什么可以省略,就是因为他能推导出来,从哪儿推导呢?肯定是函数定义的地方,函数funA在定义的时候就声明了这个函数的类型,包括参数类型,返回类型

那如果lambda有多个参数了?

fun funAA(funParam:(Int,Int)->Int){
    funParam(1,1)
}

// 必须写上参数,如果参数没有被使用,可以使用_代替
funAA { _, i2 -> i2+1 }
复制代码

多个参数就需要指明参数的名字了,如果参数没有使用,就可以使用_代替

kotlin的lambda和java的lambda的对比

java的lambda是在java8引入的,是针对单抽象SAM(Single Abstract Method)接口的简化使用,例如Runnable

// 原始写法
new Thread(new Runnable(){

    @Override
    public void run() {
        
    }
});

// 引入lambda之后
new Thread(() -> {
    int i = 1;
});

复制代码

而kotlin的lambda是一个函数对象,划重点它是一个对象,是一个函数类型的对象,所以它可以作为参数直接赋值给别的变量,而java中的lambda是做不到的,他只能用在单抽象接口的地方,只是一种写法上的变化,本质并没有改变,还是匿名内部类

kotlinでは、このSAMインターフェースに対して、互換性のある変換も実行されます。これにより、SAMインターフェースを使用する関数が直接高階関数に変換されるため、kotlinのラムダを直接適用することもできます。

Thread { val i = 1 }
复制代码

上記はkotlinの高階関数の全内容です。実際、難しいことではありませんが、最初は少し慣れていないかもしれませんが、kotlinの高階関数は非常に重要であると言う必要があります。 、特にラムダ、あなたが通常もっと書く限り、それは非常に簡単です

ビッグガイの記事を参照してください:

Kotlin高階関数

Kotlinでのラムダ式

おすすめ

転載: juejin.im/post/7085289419850121252