Kotlin 中泛型 类 的扩展函数
Extension functions for generic classes in 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
并希望实现相同的行为,e。 G。 T 被推断为 Foo
class Foo {
fun <T> T.foo(that: T): T = throw Exception()
init {
"str" foo 42
}
}
您的方法 plus
期望参数具有与接收者相同的泛型类型参数 T
。因此,您不能将 Foo<String>
添加到 Foo<Int>
.
如果您希望能够添加所有类型的 Foo
,那么您需要像这样声明您的扩展函数:
operator fun <T,R> Foo<T>.plus(that: Foo<R>): Foo<T> = throw Exception()
这个问题是泛型工作原理的核心。
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>
不是 Foo<Any>
的子类型,事实上,除了 Int
之外,没有其他类型 T
可以使 Foo<T>
成为 Foo<Int>
的超类型。所以,T
一定是Int
,但是同理(因为第二个参数)也一定是String
,所以无解,函数不适用.
您可以通过在 T
:
中设置 Foo
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
成员的可能签名施加了一些限制,但如果您同意他们的意见,它会解决您的问题。
查看此 link 了解更多详情: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 的生成泛型类型参数将根据需要加宽,但操作本身是合法的,不会引入协变。
我下面的扩展函数有什么问题
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
并希望实现相同的行为,e。 G。 T 被推断为 Foo
class Foo {
fun <T> T.foo(that: T): T = throw Exception()
init {
"str" foo 42
}
}
您的方法 plus
期望参数具有与接收者相同的泛型类型参数 T
。因此,您不能将 Foo<String>
添加到 Foo<Int>
.
如果您希望能够添加所有类型的 Foo
,那么您需要像这样声明您的扩展函数:
operator fun <T,R> Foo<T>.plus(that: Foo<R>): Foo<T> = throw Exception()
这个问题是泛型工作原理的核心。
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>
不是 Foo<Any>
的子类型,事实上,除了 Int
之外,没有其他类型 T
可以使 Foo<T>
成为 Foo<Int>
的超类型。所以,T
一定是Int
,但是同理(因为第二个参数)也一定是String
,所以无解,函数不适用.
您可以通过在 T
:
Foo
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
成员的可能签名施加了一些限制,但如果您同意他们的意见,它会解决您的问题。
查看此 link 了解更多详情: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 的生成泛型类型参数将根据需要加宽,但操作本身是合法的,不会引入协变。