以下の拡張機能の何が問題になっていますか
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
}
}
この問題は、ジェネリック医薬品がどのように機能するかという中心にあります。
class Foo {
fun <T> T.foo(that: T): T = throw Exception()
init {
"str" foo 42
}
}
これが機能するのは、コンパイラが関数のシグネチャと引数の両方に適合するT
を見つけることができるためです。これはAny
であり、関数は次のようになります。
fun Any.foo(that: Any): Any = ...
現在、String
はAny
のサブタイプであり、Int
はAny
のサブタイプであるため、この関数は引数に適用できます。
しかし、最初の例では:
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> = ...
さて、Foo
はT
で不変なので、Foo<Int>
はnotFoo<Any>
のサブタイプ。実際、Foo<T>
をFoo<Int>
のスーパータイプにするT
以外のタイプInt
はありません。したがって、T
は正確にInt
である必要がありますが、同じロジック(2番目の引数のため)でも正確にString
である必要があるため、解決策はなく、関数該当しません。
Foo
でT
co-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
Andrey Breslawが受け入れた答えは正しいと思いますが、間違った解決策を提供します。
コンパイラは、提供されたジェネリック型引数の共通スーパータイプを推測するように指示する必要があります。つまり、Fooのジェネリック型引数が共通のスーパータイプを共有している限り(そして常にそうします)、それを使用します。お気に入り:
operator fun <T, R: T, S: T> Foo<R>.plus(that: Foo<S>): Foo<T> = throw Exception()
これで、返されるFooの結果のジェネリック型引数は、型が一致しない場合に必要に応じて拡張されますが、共分散を導入することなく、操作自体は有効です。
メソッドplus
は、パラメーターがレシーバーと同じジェネリック型パラメーターT
を持つことを想定しています。したがって、Foo<String>
をFoo<Int>
に追加することはできません。
すべてのタイプのFoo
を追加できるようにする場合は、次のように拡張関数を宣言する必要があります。
operator fun <T,R> Foo<T>.plus(that: Foo<R>): Foo<T> = throw Exception()