GenericsはKotlinで汎用的で型安全なデータ構造や関数を作成することを可能にします。主な特徴は、Kotlinがコンパイル時にジェネリック型システムを実装しており、Javaと同様ですが、より厳格な型付けとvariance(共変性と逆変性)の拡張構文を持っていることです。
ジェネリクスの制限:
T()は禁止されています)。Variance:
共変性の例:
interface Producer<out T> { fun produce(): T }
逆変性の例:
interface Consumer<in T> { fun consume(item: T) }
Javaとの違い:
outは? extendsの代わり、inは? superの代わり)。?、ただしin/outのみ)。質問: 「Kotlinでは、配列の配列(Array<Array<Int>>)をArray<out Array<Int>>として宣言できますか?その場合、配列に値を書き込もうとするとどうなりますか?」
回答: はい、Array<out Array<Int>>として宣言できますが、そのような配列は読み取り専用(read-only)になります:
val arr: Array<out Array<Int>> = Array(1) { Array(1) { 0 } } arr[0] = arrayOf(1, 2, 3) // コンパイルエラー!
値を書き込もうとすると、エラーが発生します — outパラメータを持つジェネリック配列は要素を記録できません。在ると型安全性が破られます。
説話
チームでは、outパラメータ型のジェネリックオブジェクトの配列を作成しようとし、その後、set(index, value)を介して値を設定しようとしました。コードはコンパイルされましたが、ランタイムでエラーが発生し、いくつかの関数が機能しなくなりました。
説話
一度、JavaからKotlinにライブラリを移行する際、ワイルドカード型(? extends ...)を残し、Kotlinでは単に型をout/inに変更せずにコピーしました。結果は、コンパイルが失敗し、"遍歴"中にエラーがランタイムで発生し、デバッグプロセスが複雑になりました。
説話
ユーザー定義クラスでin/out変異を使用しましたが、修飾子を間違え、interface Stack<in T>をStack<out T>の代わりに宣言しました。これにより、スタックから要素を戻すことができなくなる結果となり、メソッドのシグネチャがsystem out/in契約を破ることになりました。