在 Kotlin 中安全转换为泛型类型时出现异常

Viv*_*pta 5 java casting kotlin

我在 kotlin 中使用安全转换选项,即as?当数据类型不兼容时,我仍然收到类转换异常,当我通过编写执行案例的通用方法执行此操作时,会发生这种情况,但是如果我直接执行转换,它会返回正如安全转换所预期的那样,为 null

class CastTest(val data: Any) {

   fun castViaGenericMethod(): TypeA? {
      return castToContext<TypeA>()
   }

   fun castDirectly(): TypeA? {
      return data as? TypeA
   }

   private fun <CONTEXT> castToContext(): CONTEXT? = data as? CONTEXT

}
Run Code Online (Sandbox Code Playgroud)

castViaGenericMethod()->ClassCastException当数据不是 类型时,此方法将抛出异常TypeA。 ->当无法进行强制转换时castDirectly()返回。null

请建议如何做到这一点。

Rol*_*and 3

要解决您的问题,您可以使用以下reified类型

inline fun <reified CONTEXT> castToContext() = data as? CONTEXT
Run Code Online (Sandbox Code Playgroud)

它没有按您预期工作的原因是,泛型类型在运行时被删除。

如果我们查看字节码,我们会发现无论您的CONTEXT-generic 类型写在哪里,它都会变成java/lang/Object

private final castToContext()Ljava/lang/Object;
 L0
  LINENUMBER 12 L0
    ALOAD 0
    GETFIELD CastTest.data : Ljava/lang/Object;
    DUP
    INSTANCEOF java/lang/Object // (1)
    IFNE L1                     // (1)
    POP          // (2)
    ACONST_NULL  // (2)
   L1
    ARETURN
   L2
    LOCALVARIABLE this LCastTest; L0 L2 0
    MAXSTACK = 2
    MAXLOCALS = 1
Run Code Online (Sandbox Code Playgroud)

因此,安全强制转换实际上会检查给定对象是否不是类型java/lang/Object(1),并设置要返回的值null(如果是这种情况)。但由于它是 类型java/lang/Object,因此该值只是按原样返回。然而,在调用方,字节代码如下所示:

LINENUMBER 4 L0
ALOAD 0
INVOKESPECIAL CastTest.castToContext ()Ljava/lang/Object; // the call
CHECKCAST TypeA // the type check
ARETURN
Run Code Online (Sandbox Code Playgroud)

CHECKCAST TypeA它在调用后执行附加操作castToContext,并且您会得到您的ClassCastException值,因为该值没有被取消(通用类型信息在运行时被删除)。

  • 类型擦除正是这不起作用的原因。“castToContext”函数无法访问调用站点使用的类型,因此它不执行任何转换,只是返回对象。这就是 IDE 在函数中报告未经检查的转换警告的原因。 (3认同)