java.lang.Integer不能在Kotlin中强制转换为java.lang.Long(当初始值为null时)

时间:2016-07-01 10:30:53

标签: kotlin

如果我有以下内容,它可以正常工作(即数字获得分配1000)

fun main(args: Array<String>) {
    var number: Long ? = null // or number = 0
    val simpleObject = SimpleClass()
    number = 1000
    println("Hi + $number")
}

如果我有以下内容,它可以正常工作(即数字获得分配1000)

import java.util.*

fun main(args: Array<String>) {
    var number: Long = 0
    val simpleObject = SimpleClass()
    number = simpleObject.getValue<Long>()
    println("Hi + $number")
}

class SimpleClass() {
    fun <T>getValue(): T {
        return 1000 as T
    }
}

但如果我有以下内容,则会失败

import java.util.*

fun main(args: Array<String>) {
    var number: Long? = null
    val simpleObject = SimpleClass()
    number = simpleObject.getValue<Long>()
    println("Hi + $number")
}

class SimpleClass() {
    fun <T>getValue(): T {
        return 1000 as T
    }
}

报告的错误位于number = simpleObject.getValue<Long>()

Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.Long 

为什么我初始化var number: Long ? = nullvar number: Long = 0时会有不同的结果?我有什么不对吗?

已更新

使用下面的解决方法,结果没问题。但是使用了额外的临时变量。

import java.util.*

fun main(args: Array<String>) {
    var number: Long? = null
    val simpleObject = SimpleClass()
    val temp = simpleObject.getValue<Long>()
    number = temp
    println("Hi + $number")
}

class SimpleClass() {
    fun <T>getValue(): T {
        return 1000 as T
    }
}

2 个答案:

答案 0 :(得分:5)

让我们看一下生成的字节码:

fun <T> getValue(): T {
    return 1000 as T
}

// becomes

public final getValue()Ljava/lang/Object;
   L0
    LINENUMBER 17 L0
    SIPUSH 1000
    INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer;
    CHECKCAST java/lang/Object
    ARETURN
   L1
    LOCALVARIABLE this LSimpleClass; L0 L1 0
    MAXSTACK = 1
    MAXLOCALS = 1

正如您所看到的,此方法不会 1000投放到Long它只是确保对象的类型为java/lang/Object(嗯,它是)并将1000作为Integer对象返回。

因此,您可以使用任何类型调用(注意:仅调用)此方法,这不会引发异常。但是,将结果存储在变量中会调用实际强制转换,这可能会导致ClassCastException

fun f3() {
    val simpleObject = SimpleClass()
    // L0
    //   LINENUMBER 16 L0
    //   NEW SimpleClass
    //   DUP
    //   INVOKESPECIAL SimpleClass.<init> ()V
    //   ASTORE 0

    simpleObject.getValue<SimpleClass>() // no problems
    // L1
    //   LINENUMBER 17 L1
    //   ALOAD 0
    //   INVOKEVIRTUAL SimpleClass.getValue ()Ljava/lang/Object;
    //   POP

    val number = simpleObject.getValue<SimpleClass>() // throws ClassCastException1
    // L2
    //   LINENUMBER 18 L2
    //   ALOAD 0
    //   INVOKEVIRTUAL SimpleClass.getValue ()Ljava/lang/Object;
    //   CHECKCAST SimpleClass
    //   ASTORE 1


    // L3
    //   LINENUMBER 19 L3
    //   RETURN
    // L4
    //   LOCALVARIABLE number LSimpleClass; L3 L4 1
    //   LOCALVARIABLE simpleObject LSimpleClass; L1 L4 0
    //   MAXSTACK = 2
    //   MAXLOCALS = 2
}

但为什么将结果存储为Long?会引发异常?再说一次,我们来看看字节码的差异:

var number: Long? = null              |    var number: Long = 0
                                      |
      ACONST_NULL                     |        LCONST_0
      CHECKCAST java/lang/Long        |        LSTORE 0                
      ASTORE 0                        |

                number = simpleObject.getValue<Long>() [both]

      ALOAD 1                         |
              INVOKEVIRTUAL SimpleClass.getValue ()Ljava/lang/Object; [both]
      CHECKCAST java/lang/Long        |        CHECKCAST java/lang/Number
      ASTORE 0                        |        INVOKEVIRTUAL java/lang/Number.longValue ()J
                                      |        LSTORE 0

如您所见,number: Long的字节码将函数结果转换为Number,然后调用Number.longValue以将值转换为Long({ Java中的{1}}

但是,long的字节码会将函数结果直接转换为number: Long?(Java中的Long?),这会导致Long

不确定,如果此行为记录在某处。但是,ClassCastException运算符执行不安全的转换,编译器会对其发出警告:

as

答案 1 :(得分:1)

return 1000 as T

是未经检查的强制转换,编译器会发出警告。你总是会返回一个整数,但你假装它是一个T.只有当T实际上是整数时才能正常工作。在所有其他情况下,它将失败。

如果你选择一个完全不相关的类型,比如我不知道,StringBuilder:

,这个问题的显而易见性会更加明显。
var number: StringBuilder? = null
val simpleObject = SimpleClass()
number = simpleObject.getValue<StringBuilder>()

现在您应该意识到这没有意义:您正在调用一个应该返回StringBuilder的方法,但它返回1000.