web-dev-qa-db-ja.com

Kotlinのジェネリッククラスの拡張関数

以下の拡張機能の何が問題になっていますか

class Foo<T> {
    fun <T> Foo<T>.plus(that: Foo<T>): Foo<T> = throw Exception()

    init {
        Foo<Int>() + Foo<String>()  // A receiver of type Foo<T> is required
    }
}

更新

TがAnyとして正常に推測され、同じ動作を実現したい通常の拡張関数とはなぜ異なるのでしょうか。 g。 Foo <Any>として推測されるT

class Foo {
    fun <T> T.foo(that: T): T = throw Exception()

    init {
        "str" foo 42
    }
}
13
Yaroslav

この問題は、ジェネリック医薬品がどのように機能するかという中心にあります。

class Foo {
    fun <T> T.foo(that: T): T = throw Exception()

    init {
        "str" foo 42
    }
}

これが機能するのは、コンパイラが関数のシグネチャと引数の両方に適合するTを見つけることができるためです。これはAnyであり、関数は次のようになります。

fun Any.foo(that: Any): Any = ...

現在、StringAnyのサブタイプであり、IntAnyのサブタイプであるため、この関数は引数に適用できます。

しかし、最初の例では:

class Foo<T> {
    fun <T> Foo<T>.plus(that: Foo<T>): Foo<T> = throw Exception()

    init {
        Foo<Int>() + Foo<String>()  // A receiver of type Foo<T> is required
    }
}

それはすべて異なります。そのようなTはありません。ナイーブになってAnyを試してみましょう。

fun Foo<Any>.plus(that: Foo<Any>): Foo<Any> = ...

さて、FooT不変なので、Foo<Int>notFoo<Any>のサブタイプ。実際、Foo<T>Foo<Int>のスーパータイプにするT以外のタイプIntはありません。したがって、Tは正確にIntである必要がありますが、同じロジック(2番目の引数のため)でも正確にStringである必要があるため、解決策はなく、関数該当しません。

FooTco-variantを作成することで、それを機能させることができます。

class Foo<out T> {
    fun <T> Foo<T>.plus(that: Foo<T>): Foo<T> = throw Exception()

    init {
        Foo<Int>() + Foo<String>()  // A receiver of type Foo<T> is required
    }
}

これにより、Fooのメンバーの可能な署名にいくつかの制限が課せられますが、それらに問題がなければ、問題は修正されます。

詳細については、このリンクをご覧ください: http://kotlinlang.org/docs/reference/generics.html

19
Andrey Breslav

Andrey Breslawが受け入れた答えは正しいと思いますが、間違った解決策を提供します。

コンパイラは、提供されたジェネリック型引数の共通スーパータイプを推測するように指示する必要があります。つまり、Fooのジェネリック型引数が共通のスーパータイプを共有している限り(そして常にそうします)、それを使用します。お気に入り:

operator fun <T, R: T, S: T> Foo<R>.plus(that: Foo<S>): Foo<T> = throw Exception()

これで、返されるFooの結果のジェネリック型引数は、型が一致しない場合に必要に応じて拡張されますが、共分散を導入することなく、操作自体は有効です。

4
extender

メソッドplusは、パラメーターがレシーバーと同じジェネリック型パラメーターTを持つことを想定しています。したがって、Foo<String>Foo<Int>に追加することはできません。

すべてのタイプのFooを追加できるようにする場合は、次のように拡張関数を宣言する必要があります。

operator fun <T,R> Foo<T>.plus(that: Foo<R>): Foo<T> = throw Exception()
1
Kirill Rakhman