ジェネリクス

ジェネリクスについて記載します。

ゴール

以下を理解していること。

  • ジェネリクスの使い方、使いどころ
    • ジェネリック関数
    • ジェネリック制約

ジェネリクス

  • ジェネリクスとは
    • 利用する型を仮で書いておいて、実際の型はあとから指定するという記述方法。
    • 任意の型をパラメータにしたいときはどのような書き方ができるか?
// value というプロパティを持った Container というクラスを仮定
// value の型は Any

class Container(val value: Any)

fun main(args: Array<String>) {
    // 値を入れるとき
    val intContainer: Container = Container(10)       // OK
    val strContainer: Container = Container("hello!") // OK

    // 値を取り出すとき
    // as ◯◯ をつけてキャストする必要がある
    val i = intContainer.value as Int      // OK
    val s = strContainer.value as String   // OK

    // ところがキャストは失敗する可能性がある
    val i2 = intContainer.value as String  // 実行時に ClassCastException の例外を吐かれる
}
  • 上記の例の場合、ClassCastException が出ないように作るのはプログラマの責任
    • ジェネリクスを使うと、コンパイル時に妥当かチェックしてもらえる
// any を使わず、ジェネリクスを使った場合
// value の型は <T> として仮置きしている

class Container<T>(val value: T)

fun main(args: Array<String>) {
    // 値を入れるとき
    val intContainer: Container<Int> = Container<Int>(10)

    // 値を取り出すときにキャスト不要
    // キャスト不要なので ClassCastException も起こらない
    val i: Int = intContainer.value 

    // 使い方が違っていればコンパイル時にエラーになる
    val s: String =  intContainer.value // コンパイルエラー
}
  • ジェネリック関数
    • ジェネリクスは関数、メソッド、プロパティにも適用できる
// data クラスにしてみる
data class Container<T>(val value: T)

// 関数にジェネリクスを用いる場合
fun <T> box(value: T): Container<T> {
    return Container(value)
}

val <T> T.string: String
    get() = toString()

fun main(args: Array<String>) {
    val container = box<Int>(100)
    println(container.string) // Container(value=100)
}
  • ジェネリック制約
    • ジェネリクスでは任意の型を扱うことができるが、

      「何らかのクラスのサブクラスだけに制限したい」という制限も可能
interface IFA
interface IFB
class A<T>()
class B<T : IFA>() // IFA interface を継承した T に限る

fun main(args: Array<String>) {
    A<IFA>() // OK
    A<IFB>() // OK
    B<IFA>() // OK
    B<IFB>() // error: type argument is not within its bounds: should be subtype of 'IFA'
}
  • where を使って制約を複数にすることもできる
interface IFA
interface IFB
interface IFC: IFA, IFB // IFCIFAIFB を継承

class A<T> where T : IFA, T: IFB  // TIFA のサブクラスかつ IFB のサブクラスに限る

fun main(args: Array<String>) {
    A<IFA>() // error: type argument is not within its bounds: should be subtype of 'IFB'
    A<IFB>() // error: type argument is not within its bounds: should be subtype of 'IFA'
    A<IFC>() // OK
}
  • 以下については記載を省略
    • 変位指定
    • スター投影
    • 具象型

results matching ""

    No results matching ""