Kotlin难以向上推测到推断(现场)参数

Roh*_*bhu 6 kotlin

我不确定'难以'是否是正确的词,但这是我面临的问题.我需要花费相当长的时间才能将其重现为最小的示例,所以这里有:

class BaseParameterizedType<T>

fun <U: BaseParameterizedType<*>> getSpecific(clazz: KClass<in U>) : U {
     TODO()
}

fun example(arg: KClass<out BaseParameterizedType<*>>)) {
    getSpecific(arg.innerType)
}
Run Code Online (Sandbox Code Playgroud)

好的,所以上面的代码在'TODO'失败了,但是如果它不在那里并且函数正常返回,那么它肯定会因空指针异常而失败.我努力弄清楚出了什么问题,所以我转向反编译的Java代码(来自kotlin字节码):

public static final void example(@NotNull KClass arg) {
  Intrinsics.checkParameterIsNotNull(arg, "arg");
  getSpecific(arg.getInnerType());
  throw null;  // <-- The problem
}
Run Code Online (Sandbox Code Playgroud)

如果我将函数签名更改getSpecific(clz: KClass<in U>) : U为以下任何一种形式:

  1. getSpecific(clz: KClass<out U>) : U
  2. getSpecific(clz: KClass<U>) : U
  3. getSpecific(clz: KClass<in U>) : BaseParameterizedType<*>

甚至是函数example(arg: KClass<out BaseParameterizedType<*>)或者example(arg: KClass<BaseParameterizedType<*>>),然后生成的代码是:

public static final void example(@NotNull KClass arg) {
  Intrinsics.checkParameterIsNotNull(arg, "arg");
  getSpecific(arg.getInnerType());
}
Run Code Online (Sandbox Code Playgroud)

现在,让我们说在呼叫站点,我将其更改为:

getSpecific(BaseParameterizedType::class)
Run Code Online (Sandbox Code Playgroud)

那么这也不会产生throw null条款.所以,我猜这与kotlin有关,假设这个演员总是会失败,或者有不确定的信息可以做出推断?

所以,我们知道这arg.innerTypeKClass<out BaseParameterizedType<*>>我们在一个接受的网站上使用它KClass<in BaseParameterizedType<*>>,所以为什么不U推断BaseParamterizedType<*>>.这实际上是唯一匹配的类型.

与此同时,我认为只是生成一个throw null声明是难以置信的难以调试.堆栈跟踪只会指向存在的行,getSpecific并且可以很好地确定空指针异常的来源.

hot*_*key 4

当推断类型为Nothing(并且在您的情况下)时,这是关于类型推断极端情况处理的已知问题:

在此输入图像描述

KClass<in U>由于对投影和的强制尝试,推断会以这种方式运行KClass<out BaseParameterizedType<*>>

基本上,out同时投影类型意味着in Nothing(因为实际类型参数可以是任何子类型,并且没有任何东西可以安全地传入。因此,为了KClass<out BaseParameterizedType<*>>KClass<in U>编译器匹配,选择U := Nothing,这意味着函数调用Nothing也会返回。

备注:Foo<out Any>投影不能Foo<in T>与匹配T := Any,因为传递给 的值的实际类型参数Foo<out Any>可以是,例如,Int。然后,如果在其某些函数中Foo<T>接受,允许上述匹配也将允许您将实例传递到不需要它们的地方。实际上,由于-projected 类型的未知性质,成为匹配它们的唯一方法。 TAnyFoo<Int>in Nothingout

之后,对于Nothing返回函数调用,编译器会插入该throw null字节码以确保执行不会继续(评估Nothing类型表达式应该永远不会正确完成)。

请参阅问题:KT-20849KT-18789