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:
-
getSpecific(clz: KClass) : U
-
getSpecific(clz: KClass) : U
-
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.innerType
是KClass<out BaseParameterizedType>
,我们在一个接受KClass<in BaseParameterizedType>
的站点上使用它,所以为什么不推断为BaseParamterizedType>
。 这实际上是唯一可以匹配的types。
同时,我认为只是生成一个throw null
语句难以调试是难以置信的。 stacktrace会指向getSpecific
行,并且找出空指针exception来自哪里。
这是一个已知的问题关于types推理的角落案件处理时,推断types是Nothing
(这是你的情况):
因为对KClass
和KClass
的强制尝试,推理的行为是这样的。
基本上, 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
字节码的错误,以确保执行不会继续(评估Nothing
types的expression式应该永远不会正确完成 )。
看到的问题: KT-20849 , KT-18789
就像@hotkey提到的那样, in Nothing
和Nothing
意味着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); }