修正泛型类型到第一个参数的类型

我想写一个扩展函数,它将在任何类型和接受相同类型或子类型的参数,但不是一个完全不同的类型。

我尝试过天真的做法,但没有奏效:

fun <T> Tf(x: T) { } fun main(args: Array<String>) { "1".f("1") // ok "1".f(1) // should be error } 

看来,编译器只是使用任何的T我想T被固定为接收器类型。

唯一的方法就是要告诉编译器你想要什么。

 fun <T> Tf(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".f(1)那样调用fun <T> Tf(x: T) {} ,编译器寻找一个普通的超类型的StringInt ,它是Any 。 然后它决定T is Any ,并且没有错误。 影响此过程的唯一方法是明确指定T"1".f<String>(1)

由于所有的检查都是由编译器执行的,这个问题与类型擦除无关。

你的问题就像是说:“ 约翰比卡尔大3岁,卡尔比约翰小3岁 ”……你还不知道他们的年龄,没有更多的信息。 这是你给编译器的证据类型,然后你期望它能正确猜测。 从这些信息中唯一可以得出的结论是,约翰至少3岁,卡尔至少1岁。

这种类型的假设就像编译器找到Any的常见上界一样。 它有两种强烈的文字类型可供选择,而且无法改变。 如何判断IntString是否更重要,同时你告诉它任何T的上界都是Any? 根据您的型号规格是有效的。 所以安全的答案是看文字是否符合T: Any?的标准T: Any? 当然他们也有,他们都有祖先的Any 。 即使你不想要,编译器也能满足你所​​有的条件。

如果你有打破平局的标准,这将有所不同。 例如,如果返回类型为T ,而类型为String的变量接收该值,则会影响Type推断的决定。 这例如产生一个错误:

 fun <T: Any> T.f2(x: T): T = x val something: String = "1".f2(1) // ERROR 

因为现在类型T被锚定在表达String的“左侧”,毫无疑问。

也有可能这也可能是一个类型推断问题,不打算,检查YouTrack中报告的问题或添加自己的从编译器团队得到明确的答案。 我添加了一个功能请求KT-13138用于锚定特定的类型参数,以了解团队如何响应。

您可以通过将f为返回可调用对象的扩展属性来将T修复为接收方类型:

 val <T> Tf: (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")目前会导致一个错误:“类型不匹配:推断类型是字符串,但T是预期的”。 这是一个编译器问题。 见KT-10364 。 另见KT-13139 。 您可以投票和/或观看更新的问题。 在解决此问题之前,您仍然可以执行以下操作:

 "1".f.invoke("1") /* or */ ("1".f)("1") /* or */ val f = "1".f f("1")