コトリンのラムダ式

コトリンのラムダ式

1. 概要

この記事では、Kotlin言語でラムダを探索します。 ラムダはKotlinに固有のものではなく、他の多くの言語で長年使用されてきたことに留意してください。

Lambdas Expressions are essentially anonymous functions that we can treat as values –たとえば、メソッドへの引数としてそれらを渡したり、返したり、通常のオブジェクトで実行できるその他のことを実行したりできます。

2. ラムダの定義

後で説明するように、KotlinLambdaはJavaLambdaと非常によく似ています。 Java Lambdaの操作方法といくつかのベストプラクティスhereについて詳しく知ることができます。

ラムダを定義するには、構文に固執する必要があります。

val lambdaName : Type = { argumentList -> codeBody }

オプションではないラムダの唯一の部分は、 codeBody。

最大で1つの引数を定義する場合、引数リストはスキップでき、Type canはKotlinコンパイラによって推測されることがよくあります。 We don’t always need a variable as well, the lambda can be passed directly as a method argument.

ラムダブロック内の最後のコマンドのタイプは、返されるタイプです。

2.1. 型推論

Kotlinの型推論により、ラムダの型をコンパイラーが評価できます。

数の二乗を生成するラムダを書くと、次のようになります。

val square = { number: Int -> number * number }
val nine = square(3)

Kotlinは、上記の例を1つのInt を取り、Int:(Int) → Intを返す関数であると評価します。

単一の引数番号に100を掛けるラムダを作成したい場合は、その値をString:として返します。

val magnitude100String = { input : Int ->
    val magnitude = input * 100
    magnitude.toString()
}

Kotlinは、このラムダがタイプ(Int) → Stringであることを理解します。

2.2. 型宣言

Kotlinが型を推測できない場合があり、ラムダの型を明示的に宣言する必要があります。他のタイプと同じように。

パターンはinput → outputですが、コードが値を返さない場合は、タイプUnitを使用します。

val that : Int -> Int = { three -> three }
val more : (String, Int) -> String = { str, int -> str + int }
val noReturn : Int -> Unit = { num -> println(num) }

ラムダをクラス拡張として使用できます。

val another : String.(Int) -> String = { this + it }

ここで使用するパターンは、定義した他のラムダとわずかに異なります。 ブラケットにはまだ引数が含まれていますが、ブラケットの前に、このラムダをアタッチするタイプがあります。

String からこのパターンを使用するには、Type.lambdaName(arguments)soを呼び出して、「別の」例を呼び出します。

fun extendString(arg: String, num: Int) : String {
    val another : String.(Int) -> String = { this + it }

    return arg.another(num)
}

2.3. ラムダから戻る

最終式は、ラムダが実行された後に返される値です。

val calculateGrade = { grade : Int ->
    when(grade) {
        in 0..40 -> "Fail"
        in 41..70 -> "Pass"
        in 71..100 -> "Distinction"
        else -> false
    }
}

最後の方法は、匿名関数の定義を活用することです。引数と戻り値の型を明示的に定義する必要があり、メソッドと同じようにreturnステートメントを使用できます。

val calculateGrade = fun(grade: Int): String {
    if (grade < 0 || grade > 100) {
        return "Error"
    } else if (grade < 40) {
        return "Fail"
    } else if (grade < 70) {
        return "Pass"
    }

    return "Distinction"
}

3. it

単一引数ラムダの省略形は、キーワード ‘it'を使用することです。 This value represents any lone that argument we pass to the lambda function.

次のIntsの配列に対して、同じforEach メソッドを実行します。

val array = arrayOf(1, 2, 3, 4, 5, 6)

最初にラムダ関数のロングハンド形式を見てから、同じコードのショートハンド形式を見ていきます。ここで、「it」は次の配列の各要素を表します。

ロングハンド:

array.forEach { item -> println(item * 4) }

速記:

array.forEach { println(it * 4) }

4. ラムダの実装

スコープ内にあるラムダを呼び出す方法と、ラムダを引数として渡す方法について簡単に説明します。

ラムダオブジェクトがスコープ内に入ったら、名前の後に角かっこと引数を使用して、他のスコープ内メソッドとして呼び出します。

fun invokeLambda(lambda: (Double) -> Boolean) : Boolean {
    return lambda(4.329)
}

ラムダを引数として高次メソッドに渡す必要がある場合、5つのオプションがあります。

4.1. Lambdaオブジェクト変数

セクション2で宣言されている既存のラムダオブジェクトを使用して、他の引数と同じようにオブジェクトをメソッドに渡します。

@Test
fun whenPassingALambdaObject_thenCallTriggerLambda() {
    val lambda = { arg: Double ->
        arg == 4.329
    }

    val result = invokeLambda(lambda)

    assertTrue(result)
}

4.2. ラムダリテラル

ラムダを変数に割り当てる代わりに、リテラルをメソッド呼び出しに直接渡すことができます。

Test
fun whenPassingALambdaLiteral_thenCallTriggerLambda() {
    val result = invokeLambda({
        true
    })

    assertTrue(result)
}

4.3. ブラケット外のラムダリテラル

JetBrainsが推奨するラムダリテラルの別のパターンは、ラムダをメソッドの最後の引数として渡し、ラムダをメソッド呼び出しの外側に配置することです。

@Test
fun whenPassingALambdaLiteralOutsideBrackets_thenCallTriggerLambda() {
    val result = invokeLambda { arg -> arg.isNaN() }

    assertFalse(result)
}

4.4. メソッド参照

最後に、メソッド参照を使用するオプションがあります。 これらは、既存のメソッドへの参照です。

以下の例では、Double::isFiniteを取ります。 その関数はラムダと同じ構造を取りますが、引数が1つあり、Double を受け取り、Booleanを返すため、タイプはKFunction1<Double, Boolean> です。

@Test
fun whenPassingAFunctionReference_thenCallTriggerLambda() {
    val reference = Double::isFinite
    val result = invokeLambda(reference)

    assertTrue(result)
}

5. JavaのKotlin Lambda

Kotlinは、生成された関数インターフェイスを使用してJavaと相互運用します。 それらはKotlinソースコードhereに存在します。

これらの生成されたクラスで渡すことができる引数の数には制限があります。 現在の制限は22です。インターフェイスFunction22で表されます。

Function interfaceのジェネリックスの構造は、数とがラムダへの引数の数を表すことであり、そのクラスの数が順番に引数の型になります。

最後の一般的な引数は戻り値の型です:

import kotlin.jvm.functions.*

public interface Function1 : Function {
    public operator fun invoke(p1: P1): R
}

When there is no return type defined within the Kotlin code, then the lambda returns a Kotlin Unit Javaコードは、kotlin packageからクラスをインポートし、nullで返す必要があります。

以下は、KotlinとJavaの一部であるプロジェクトからKotlin Lambdaを呼び出す例です。

import kotlin.Unit;
import kotlin.jvm.functions.Function1;
...
new Function1() {
    @Override
    public Unit invoke(Customer c) {

        AnalyticsManager.trackFacebookLogin(c.getCreated());

        return null;
    }
}

Java8を使用する場合、Function anonymousクラスの代わりにJavaラムダを使用します。

@Test
void givenJava8_whenUsingLambda_thenReturnLambdaResult() {
    assertTrue(LambdaKt.takeLambda(c -> c >= 0));
}

6. 匿名の内部クラス

Kotlinには、匿名内部クラスを扱う2つの興味深い方法があります。

6.1. オブジェクト式

Kotlin内部匿名クラスまたは複数のメソッドで構成されるJava匿名クラスを呼び出す場合、オブジェクト式を実装する必要があります。

これを示すために、単純なインターフェースと、そのインターフェースの実装を取り、Boolean argumentに依存するメソッドを呼び出すクラスを取り上げます。

class Processor {
    interface ActionCallback {
        fun success() : String
        fun failure() : String
    }

    fun performEvent(decision: Boolean, callback : ActionCallback) : String {
        return if(decision) {
            callback.success()
        } else {
            callback.failure()
        }
    }
}

匿名の内部クラスを提供するには、「オブジェクト」構文を使用する必要があります。

@Test
fun givenMultipleMethods_whenCallingAnonymousFunction_thenTriggerSuccess() {
    val result = Processor().performEvent(true, object : Processor.ActionCallback {
        override fun success() = "Success"

        override fun failure() = "Failure"
    })

    assertEquals("Success", result)
}

6.2. ラムダ式

一方、代わりにラムダを使用するオプションもあります。 Using lambdas in lieu of an Anonymous Inner Class has certain conditions:

  1. このクラスはJavaインターフェースの実装です(Kotlinのものではありません)

  2. インターフェイスには最大値が必要です

これらの条件の両方が満たされる場合、代わりにラムダ式を使用できます。

ラムダ自体は、インターフェースの単一メソッドと同じ数の引数を取ります。

一般的な例は、標準のJavaConsumer:の代わりにラムダを使用することです。

val list = ArrayList(2)

list.stream()
  .forEach({ i -> println(i) })

7. Conclusion

構文的には似ていますが、KotlinとJavaラムダは完全に異なる機能です。 Java 6を対象とする場合、KotlinはラムダをJVM 1.6内で利用できる構造に変換する必要があります。

それにもかかわらず、Java 8ラムダのベストプラクティスが引き続き適用されます。

ラムダのベストプラクティスhereの詳細。

コードスニペットは、いつものように、over on GitHubで見つけることができます。