English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Kotlin ガジェット

汎型は「パラメータ化型」と呼ばれ、型引数をパラメータ化し、クラス、インターフェース、メソッドに使用できます。

Javaと同様に、Kotlinも汎型を提供しており、型安全を確保し、型強制の煩わしさを解消します。

汎型クラスの宣言

class Box<T>(t: T) {
    var value = t
}

クラスの生成例を作成する際には、型引数を指定する必要があります:

val box: Box<Int> = Box<Int>(1)
// または
val box = Box(1) // コンパイラが型推定を行います1 型Int、そのためコンパイラはBox<Int>を指していると認識します。

以下の例では、汎型クラスBoxに整型データと文字列を渡します:

class Box<T>(t: T) {
    var value = t
}
fun main(args: Array<String>) {
    var boxInt = Box<Int>(10)
    var boxString = Box<String>("w3codebox")
    println(boxInt.value)
    println(boxString.value)
}

出力結果は:

10
w3codebox

汎型型変数を定義する際は、完全な型引数を指定できます。コンパイラが自動的に型引数を推定できる場合は、型引数を省略することもできます。

Kotlinの汎型関数の宣言はJavaと同じで、型引数は関数名の前に配置されます:

fun <T> boxIn(value: T) = Box(value)
// 以下はすべて合法な文です
val box4 = boxIn<Int>(1)
val box5 = boxIn(1)     // コンパイラが型推定を行います

汎型関数の呼び出し時、型引数が推定可能であれば、汎型引数を省略できます。

以下の例では、doPrintlnという汎型関数を生成しました。関数は引数の型に応じて処理を行います:

fun main(args: Array<String>) {
    val age = 23
    val name = "w3codebox"
    val bool = true
    doPrintln(age)    // 整型
    doPrintln(name)   // 文字列
    doPrintln(bool)   // ブール型
}
fun <T> doPrintln(content: T) {
    when (content) {
        is Int -> println("整数型の数字は $content")
        is String -> println("文字列を大文字に変換します:${content.toUpperCase()}")
        else -> println("Tは整数型でも文字列でもありません")
    }
}

出力結果は:

整数型の数字は 23
文字列を大文字に変換します:w3codebox
Tは整数型でも文字列でもありません

泛型制約

泛型制約を使用して、特定のパラメータが使用できる型を設定できます。

Kotlinでは、:を使用して泛型の型上限を制約します。

最も一般的な制約は上界(upper bound)です:

fun <T : Comparable<T>> sort(list: List<T>) {
    // ……
}

ComparableのサブタイプはTに代用できます。例えば:

sort(listOf(1, 2, 3)) // OK。IntはComparable<Int>のサブタイプです
sort(listOf(HashMap<Int, String>())) // エラー:HashMap<Int, String> はComparable<HashMap<Int, String>>のサブタイプではありません

デフォルトの上界はAny?です。

複数の上界制約条件がある場合、where子句を使用できます:

fun <T> copyWhenGreater(list: List<T>, threshold: T): List<String>
    where T : CharSequence,
          T : Comparable<T> {
    return list.filter { it > threshold }.map { it.toString() }
}

型変化

Kotlinにはワイドカーソル型はなく、他の2つのものがあります:宣言处的型変化(declaration-site variance(サイトバリアンス)と型投影(type projections)。

宣言处的型変化

宣言处的类型変化を使用するコバリントアノテーション修飾子:in、out、消費者 in、生産者 out。

outを使用して、型パラメータをコバリントにします。コバリント型パラメータは、出力のみとして使用でき、リターン値の型として使用できますが、入力の型として使用できません:

// クラスをコバリントサポートする定義
class w3codebox<out A>(val a: A) {
    fun foo(): A {
        return a
    }
}
fun main(args: Array<String>) {
    var strCo: w3codebox<String> = w3codebox("a")
    var anyCo: w3codebox<Any> = w3codebox<Any>("b")
    anyCo = strCo
    println(anyCo.foo())   // aを出力します
}

inは、タイプパラメータを逆コバリントにします。逆コバリントタイプパラメータは、入力パラメータとしてのみ使用でき、返り値のタイプとしては使用できません:

// 逆コバリントをサポートするクラスを定義します
class w3codebox<in A>(a: A) {
    fun foo(a: A) {
    }
}
fun main(args: Array<String>) {
    var strDCo = w3codebox("a")
    var anyDCo = w3codebox<Any>("b")
    strDCo = anyDCo
}

スター投射

あるときは、タイプパラメータの情報がわからない場合でもそれを使用したいことがあります。ここで「安全に使用」というのは、泛型タイプに対してタイプ投射を定義し、その泛型タイプのすべての実体例がその投射のサブタイプであることを要求することを指します。

この問題に対して、Kotlinはスター投射(satar projection)と呼ばれる文法を提供しています。-projection):

  • タイプがFoo<out T>と定義されており、Tがコバリント型パラメータで上界(upper bound)がTUpperである場合、Foo<>はFoo<out TUpper>に等価であり、Tが未知のとき、Foo<>からTUpperタイプの値を読み取ることができます。

  • タイプがFoo<in T>と定義されており、Tが逆コバリント型パラメータである場合、Foo<>はFoo<in Nothing>に等価であり、Tが未知のとき、Foo<>に書き込むことができます。

  • タイプがFoo<T>と定義されており、Tがコバリント型パラメータで上界(upper bound)がTUpperである場合、読み取りの際にはFoo<*>はFoo<out TUpper>に等価であり、値を書き込む場合に等価です。

泛型タイプが複数のタイプパラメータを持っている場合、各タイプパラメータは独立に投射することができます。例えば、タイプがinterface Function<in T, out U>と定義されている場合、以下のようなスター投射が可能です:

  • Function<*, String> , 代表 Function<in Nothing, String> ;

  • Function<Int, *> , 代表 Function<Int, out Any?> ;

  • Function<, > , 代表 Function<in Nothing, out Any?> .

注意: 星号投射は Java の原生型(raw type)と非常に似ているが、安全に使用できます