Kotlin の拡張機能を差別化する

David Mbochi Njonge 2023年1月30日
  1. Kotlin で拡張関数を作成する
  2. Kotlin の拡張機能で Lambda 関数を使用する
  3. Kotlin で let() 拡張関数を使用する
  4. Kotlin で also() 拡張関数を使用する
  5. Kotlin で apply() 拡張関数を使用する
  6. Kotlin で takeIf() 拡張関数を使用する
  7. Kotlin で takeUnless() 拡張関数を使用する
  8. まとめ
Kotlin の拡張機能を差別化する

オブジェクト指向のプログラミングでは、継承、カプセル化、抽象化、および多態性の概念を学びましたが、これらは、柔軟で再利用可能で保守可能な方法でアプリケーションを構造化するのに十分ではありません。

設計パターンは、設計原則と OOP 概念の助けを借りて、一般的な設計問題の解決策を紹介するために助けになりました。このチュートリアルで取り上げるデザインパターンと非常によく似たデザインパターンの 1つに、デコレータのデザインパターンがあります。

デコレータの設計パターンにより、別のクラスから拡張することなく、アプリケーションに機能を追加できます。Kotlin では、Kotlin 拡張機能を使用して、同じ機能設計パターンを実現できます。

Kotlin の拡張機能は、別のクラスから拡張したり、デコレータのデザインパターンを使用したりすることなく、クラスに機能を追加します。舞台裏では、拡張関数は汎用であり、ラムダ関数を使用して前述の機能を実現できます。

このチュートリアルでは、各拡張機能について深く掘り下げ、それぞれの例を示します。この記事で取り上げる拡張機能には、apply()also()let()takeIf()、および takeUnless() が含まれます。

Kotlin で拡張関数を作成する

拡張関数を作成するには、String、Number、クラスなど、機能を追加するタイプを定義し、ドット演算子を使用して機能を委任するメソッドを定義します。拡張関数の戻りタイプを定義し、最後に同じ行にメソッドのロジックを実装します。

Intellij コードエディタに移動し、extension-exercise という名前の新しいプロジェクトを作成します。src の下に、フォルダ構造 com.extension を作成します。

extension フォルダの下に Main.kt ファイルを作成し、次のコードを貼り付けます。

package com.extension

fun Number.isGreaterThanTen(): Boolean = this.toInt() > 10;

fun main() {
    val  num = 100;
    println(num.isGreaterThanTen());
}

上記のコードでは、数値が 10 より大きいかどうかをチェックし、ブール値を返す、型 Number に isGreaterThanTen() という名前の拡張関数を定義しました。

この時点以降、タイプ番号の変数を呼び出すと、上記のメインメソッドに示すように、isGreaterThanTen() メソッドにアクセスできるようになります。

上記のプログラムを実行すると、ブール値 true がコンソールに記録されることに注意してください。

true

Kotlin の拡張機能で Lambda 関数を使用する

舞台裏では、ラムダ関数は 1つのメソッドのみを含むインターフェイスとして定義できます。ラムダ関数は、関数名を指定せずに匿名で具体的な関数に渡され、矢印記号を使用して戻り型が定義されます。

別のファイルを作成し、前のコードをファイルに移動します。次の例をコピーして、空の Main.kt ファイルに貼り付けます。

package com.extension

fun Number.isGreaterThanTen(block: (Number) -> Boolean): Boolean = block(this);

fun main() {
    val num = 100;
    println(num.isGreaterThanTen { number ->
        number.toInt() > 10
    })

}

上記のコードでは、ラムダ関数をパラメーターとして受け取り、ブール値を返す isGreaterThanTen() という名前の拡張関数を定義しました。

block という名前のラムダ関数は、Number 型のパラメーターを受け入れ、ブール値を返します。ラムダ関数によって返されるこのブール値は、拡張関数に委任されます。

前の例と比較して、メイン関数内の Number 型の任意の変数で isGreaterThanTen() を呼び出した後、拡張関数のロジックを定義します。

上記のコードを実行して、前の例と同じ結果が得られることを確認します。出力は以下のようになります。

true

Kotlin で let() 拡張関数を使用する

前のコードを作成した新しいファイルに移動し、次のコードを Main.kt ファイルに貼り付けます。

package com.extension

inline fun <T, R> T.let(block: (T) -> R): R = block(this);

fun  main(){
    val num = 10;
    val result = num.let { number -> number.toString() }
    println(result);
    println(result::class.java.typeName);
}

let() 関数はジェネリックを使用して定義されます。つまり、任意のタイプを使用できます。let() 関数を呼び出すオブジェクトは、ラムダ関数のパラメーターとして渡されます。

ラムダ関数は let() 関数内で定義され、パラメーターとして渡されたものとは異なるオブジェクトを返します。

let() 関数によって返される値はラムダ関数によって返される関数と同じであるため、ラムダ関数の戻り値を let() 関数に委任しました。

main メソッドでは、Number 型の変数を定義し、拡張関数を呼び出して、数値の文字列表現を返しました。

次に、返された値とそのタイプをコンソールに記録して、拡張機能が必要に応じて機能していることを確認しました。以下に示すように、拡張関数に渡される型は数値でしたが、返される値は文字列であることに注意してください。

10
java.lang.String

Kotlin で also() 拡張関数を使用する

前のコードを作成した新しいファイルに移動し、次のコードを空の Main.kt ファイルに貼り付けます。

package com.extension

inline fun <T> T.also(block: (T) -> Unit): T {block(this); return  this}

fun main(){
    val  num = 10;
    num.also {i ->
        println(i == 10)
    }
}

also() 拡張関数を呼び出すオブジェクトは、ラムダ関数のパラメーターとして渡されます。ラムダ関数は拡張関数の引数として定義され、通常は Unit で示される値を返しません。

also() 関数は、使用している現在の変数を返します。これは、さらに計算するためにラムダ関数に委任されます。main 関数では、値が 10 に等しいかどうかをチェックするために、ラムダ関数に委任されたタイプ Number の変数を定義しました。

以下に示すように、コードを実行して、真の値を返すことに注意してください。

true

Kotlin で apply() 拡張関数を使用する

前のコードを作成した新しいファイルに移動し、次のコードを空の Main.kt ファイルに貼り付けます。

package com.extension

inline fun <T> T.apply(block: T.() -> Unit): T {block(); return this}

fun main(){
    val num = 10;
    num.apply {
        println(toDouble());
    }
}

apply() 拡張関数を呼び出すオブジェクトは、このオブジェクトのメソッド呼び出しとしてラムダ関数のパラメーターに渡されます。ラムダ関数には戻り値がありません。

定義は、ラムダ関数が機能を処理するため、メソッドを呼び出すために型のオブジェクトを使用する必要がないことを意味します。その場合、必要なメソッドを型から呼び出すだけで済みます。

main 関数では、Number 型の変数を定義し、apply() メソッドを使用して、toDouble() メソッドを呼び出して Number を Double に変換しました。toDouble() メソッドの呼び出しにオブジェクトへの参照が使用されていないことに注意してください。

上記のコードを実行し、以下に示すように、コンソールに記録される値が Double であることに注意してください。

10.0

Kotlin で takeIf() 拡張関数を使用する

前のコードを作成した新しいファイルに移動し、次のコードを空の Main.kt ファイルに貼り付けます。

package com.extension

inline fun <T> T.takeIf(predicate: (T) -> Boolean): T? = if (predicate(this)) this else null

fun main() {
    val  num = 10;
    println(num.takeIf { i ->
        i.plus(10) < 30
    })
}

takeIf() 拡張関数を呼び出すオブジェクトは、ラムダ関数のパラメーターとして渡されます。ラムダ関数はこのメソッドの引数として渡され、ブール値を返します。

拡張関数は、ラムダ式の条件が true と評価された場合にのみ、呼び出し元のオブジェクトを返します。それ以外の場合は、null 値を返します。

メイン関数では、takeIf() を使用してこの数値に 10 を加えた値が 30 未満であるかどうかを判断する Number 型の値を定義しました。条件が true と評価された場合、値 10 が返されます。それ以外の場合は、null 値が返されます。

上記のコードを実行し、ラムダ式が true と評価され、以下に示すように値 10 が返されることに注意してください。

10

Kotlin で takeUnless() 拡張関数を使用する

前のコードを作成した新しいファイルに移動し、次のコードをコピーして空の Main.kt ファイルに貼り付けます。

package com.extension

inline fun <T> T.takeUnless(predicate: (T) -> Boolean): T? = if (!predicate(this)) this else null

fun main() {
    val num = 10;
    println(num.takeUnless { i ->
        i.plus(10) < 30
    })
}

takeUnless() 拡張関数を呼び出すオブジェクトは、ラムダ関数のパラメーターとして渡されます。ラムダ関数はこのメソッドで定義され、ブール値を返します。

takeIf()takeUnless() の違いは、この例では、ラムダ式が false と評価された場合にのみ値が返されることです。これは、拡張関数によって返される値が、ラムダ関数によって返されるブール関数の逆であることを意味します。

main メソッド内で、上記と同じ例を定義しましたが、ラムダ関数が true と評価され、false に反転されるため、この例は null を返すことに注意してください。

null

まとめ

このチュートリアルでは、拡張機能を使用して Kotlin に機能を追加する方法を説明しました。対象となる拡張機能には、let()also()apply()takeIf()、および takeUnless() の使用が含まれます。

これらの拡張機能は Kotlin API ですでに定義されており、このチュートリアルで行ったように定義する必要はありません。

David Mbochi Njonge avatar David Mbochi Njonge avatar

David is a back end developer with a major in computer science. He loves to solve problems using technology, learning new things, and making new friends. David is currently a technical writer who enjoys making hard concepts easier for other developers to understand and his work has been published on multiple sites.

LinkedIn GitHub