我想编写一个扩展函数,该函数可用于任何类型并接受相同类型或子类型的参数,但不是完全不同的类型。
我尝试过天真的方法,但它没有工作:
fun <T> T.f(x: T) {
}
fun main(args: Array<String>) {
"1".f("1") // ok
"1".f(1) // should be error
}
似乎编译器只使用Any for T.我希望T固定为接收器类型。
答案 0 :(得分:4)
唯一的方法是告诉编译器你想要什么。
fun <T> T.f(x: T) {
}
为了使用它,你必须告诉Kotlin你想要的类型。
"1".f<String>("2") // Okay
"1".f(2) // Okay (see voddan's answer for a good explanation)
"1".f<String>(2) // Fails because 2 isn't a String
"1".f<Int>(2) // Fails because "1" isn't an Int
答案 1 :(得分:3)
当你像fun <T> T.f(x: T) {}
一样调用"1".f(1)
时,编译器会查找String
和Int
的常见超类型,即Any
。然后它决定T is Any
,并且不发出错误。影响此流程的唯一方法是明确指定T
:"1".f<String>(1)
由于所有检查都是由编译器执行的,因此该问题与类型擦除无关。
答案 2 :(得分:3)
你的问题就像说&#34; 约翰比卡尔年长3岁,卡尔比约翰小3岁&#34; ......如果没有更多信息,你仍然不知道他们的年龄。这是您给编译器提供的证据类型,然后您希望它能够正确猜测。你可以从这些信息中得到的唯一真相是,约翰至少3岁,卡尔至少1天。
这种类型的假设就像编译器找到Any
的共同上限一样。它有两种强大的文字类型可供选择,也无法改变。它将如何决定Int
或String
是否更重要,同时您告诉它T
的上限为Any?
是有效的,因为您的类型规格。因此,安全的答案是看两个文字是否符合T: Any?
的标准,当然它们都是,它们都有Any
的祖先。编译器符合您的所有条件,即使您不想要它。
如果你有打破平局的标准,这将有所不同。例如,如果返回类型为T
且类型为String
的变量接收该值,则会影响类型推断的决策。例如,这会产生错误:
fun <T: Any> T.f2(x: T): T = x
val something: String = "1".f2(1) // ERROR
因为现在类型T
由&#34;左侧&#34;锚定。期待String
毫无疑问的表达。
这也可能是一个非预期的类型推断问题,检查YouTrack中报告的问题或添加自己的问题以从编译器团队获得明确的答案。我添加了一个功能请求KT-13138,用于锚定特定类型参数以查看团队如何响应。
答案 3 :(得分:1)
您可以通过使T
和extension property返回一个可调用对象来将f
修复为接收器类型:
val <T> T.f: (T) -> Unit
get() = { x -> }
fun main(vararg args: String) {
"1".f("1") // will be OK once KT-10364 is resolved
"1".f(1) // error: The integer literal does not conform to the expected type String
}
不幸的是"1".f("1")
目前导致错误:&#34;类型不匹配:推断类型是字符串但是T是预期的&#34;。这是编译器问题。见KT-10364。另见KT-13139。您可以投票和/或观看更新问题。在修复此问题之前,您仍然可以执行以下操作:
"1".f.invoke("1")
/* or */
("1".f)("1")
/* or */
val f = "1".f
f("1")