Kotlin-为什么我们必须为泛型方法显式地指定类型参数?

时间:2018-12-06 10:06:03

标签: kotlin

我正在研究像这样的扩展方法:

infix fun <T> T.isNullOr(other: T): Boolean {
    if (this == null) return true
    return this == other
}


并且我正在尝试使用这种方法。

val thisShouldWork = true isNullOr true // this is true
val thisShouldNotWork = true isNullOr 0 // No compilation errors?


我期望编译错误,因为Boolean的type参数自动设置为isNullOr,但不是。发生什么事了?

我误会吗?


在C#中,相同的代码可以正常工作。

static bool IsNullOr<T>(this T t, T other) {
    if (t == null) return true;
    return Equals(t, other);
}

bool howAboutThis = 0.IsNullOr(0);
bool andThis = 0.IsNullOr(false); // error - cannot detect type parameter for this

5 个答案:

答案 0 :(得分:3)

在这里,val thisShouldNotWork = true isNullOr 0等于val thisShouldNotWork: Boolean = true.isNullOr<Any>(0)。类型参数推断为最接近的父级。

函数的返回类型基于逻辑表达式求值this == other。让我们看一下==函数声明:public open operator fun equals(other: Any?): Boolean。它收到Any?

此函数中的Type参数与Boolean无关。

答案 1 :(得分:1)

我认为在这种情况下,泛型并不重要。您只需在方法中调用equals,即可对任何类型进行调用。它基本上与:

infix fun Any.isNullOr(other: Any): Boolean {
    return this == other
}

它可以毫无问题地进行编译,因为您可以随时调用equals

答案 2 :(得分:1)

请记住,泛型类型信息会在运行时删除,并且每当您尝试将某种东西放入接受泛型的方法中时,都将使用公共分母,例如:

listOf("one", 123) // -> assumes T:Any and therefore gives List<Any>

在您的示例中,这意味着"one".isNullOr(123)都变成Any

不过,作为旁注,如果您声明一个特定类型(例如List<String>),如下所示,则不能为它分配其他类型:

val test : List<String> = listOf(123) // this will not work

在编译时已经知道给定的int不能成为字符串。但是,此示例对您没有帮助,因为您没有返回该泛型类型。如果您的方法看起来有些不同,例如将具有通用类型作为返回值,它可能很容易像以前的List-sample一样得出结果。

因此,要修复示例,您需要指定基本上会使infix过时的类型,例如以下将按您期望的那样工作:

val someString : String? = TODO()
val works = someString.isNullOr<String?>("other")
val doesntWork = someString.isNullOr<Int?>(123) // does not nor does:
val doesntWorkToo = someString.isNullOr<String?>(123)

请注意,对于您所展示的内容,某些标准功能可能会有所帮助(但不能消除该特定问题),即,将?: (elvis operator)?.let一起使用:

val someVal : String? = "someString given from somewhere"
val thisWorks = someVal?.let { 
                     it == "some other string to compare" 
                } ?: true /* which basically means it was null */
val thisWillNot = someVal?.let {
                     it == 123 // compile error (funny enough: it.equals(123) would work ;-)
                  } ?: true /* it is null */

答案 3 :(得分:0)

感谢答案。我认为没有办法在编译级别阻止这种情况,因此我决定检查other的类型。

inline infix fun <reified T> T.isNullOr(other: T): Boolean {
    if (this == null) return true
    if (other !is T) return false
    return this == other
}

答案 4 :(得分:0)

如果您真的想阻止它,可以:

class IsNullOr<T>(val x: T) {
    operator fun invoke(other: T): Boolean {        
        if (x == null) return true
        return x == other
    }
}

fun <T> T.isNullOr() = IsNullOr(this)

fun main(args: Array<String>) {
    val thisShouldWork = true.isNullOr()(true) // compiles
    val thisShouldNotWork = true.isNullOr()(0) // doesn't compile
}

这使得类型推断仅取决于isNullOr的接收者。如果val是通用的,您甚至可以保留原始语法(但不能保留)。