Kotlin难以上传到推断的(现场)参数

我不确定是否“失败”是正确的话,但这是我面临的问题。 我花了相当长的时间来重现这个最小的例子,所以在这里:

class BaseParameterizedType fun <U: BaseParameterizedType> getSpecific(clazz: KClass) : U { TODO() } fun example(arg: KClass<out BaseParameterizedType>)) { getSpecific(arg.innerType) } 

好的,所以上面的代码在’TODO’失败,但是如果它不在那里,并且函数正常返回,那么它肯定会失败,并产生空指针exception。 我试图弄清楚什么是错误的,所以我转向了反编译的Java代码(来自kotlin字节码):

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

如果我将getSpecific(clz: KClass) : U的函数签名更改为以下任何一种forms:

  1. getSpecific(clz: KClass) : U
  2. getSpecific(clz: KClass) : U
  3. getSpecific(clz: KClass) : 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()); } 

现在,让我们说在呼叫站点,我改变它:

 getSpecific(BaseParameterizedType::class) 

那么这也不会生成throw null子句。 所以,我猜这与kotlin有关,假设这个演员总是会失败,或者有不确定的信息可以进行推理。

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

同时,我认为只是生成一个throw null语句难以调试是难以置信的。 stacktrace会指向getSpecific行,并且找出空指针exception来自哪里。

这是一个已知的问题关于types推理的角落案件处理时,推断types是Nothing (这是你的情况):

在这里输入图像说明

因为对KClassKClass>的强制尝试,推理的行为是这样的。

基本上, out -projectedtypes在同一时间意味着in Nothing (因为实际的types参数可以是任何的子types,没有什么可以安全地传入)。 因此, KClass>KClass匹配,编译器将选择U := Nothing ,这意味着函数调用也会返回Nothing

备注: Foo投影不能与Foo中的Foo Foo匹配, T := Any ,因为为Foo传递的值的实际types参数可以是Int 然后,如果Foo在其某些函数中接受T ,允许上述匹配也允许将Any实例传递到Foo不指望它们的地方。 实际上, in Nothing成为与它们相匹配的唯一方法的情况下,由于未被投射的types的未知性质。

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

看到的问题: KT-20849 , KT-18789

就像@hotkey提到的那样, in NothingNothing意味着Nothing都不会抛出null。所以我做了一些这样的测试:

 fun main(args: Array) { tryToReturnNothing() } fun tryToReturnNothing(): Nothing{ TODO() } 

生成 – >

  public static final void main(@NotNull String[] args) { Intrinsics.checkParameterIsNotNull(args, "args"); tryToReturnNothing(); throw null; // here } @NotNull public static final Void tryToReturnNothing() { throw (Throwable)(new NotImplementedError((String)null, 1, (DefaultConstructorMarker)null)); } 

考虑到null的types是Nothing? ,我们可以返回Nothing? 而不是Nothing 。 所以我把U换成U? ,然后throw null子句消失:

 fun > getSpecific(clazz: KClass) : U? { // see here: change U to U? TODO() } fun example(arg: KClass>) { getSpecific(arg) } 

生成 – >

  @Nullable public static final BaseParameterizedType getSpecific(@NotNull KClass clazz) { Intrinsics.checkParameterIsNotNull(clazz, "clazz"); throw (Throwable)(new NotImplementedError((String)null, 1, (DefaultConstructorMarker)null)); } public static final void example(@NotNull KClass arg) { Intrinsics.checkParameterIsNotNull(arg, "arg"); getSpecific(arg); }