2016-07-01 46 views
4

如果我有以下的,它的工作原理(即数获得分配1000)java.lang.Integer中不能转换为java.lang.Long中在科特林(当初始值为null)

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 
    } 
} 

回答

4

让我们看看到生成的字节码:

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 

,你可以SE e,此方法不会将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一个Longlong在Java中)

然而,number: Long?字节码直接投射功能结果到Java中的Long?Long)导致ClassCastException

不确定,如果此行为记录在某处。然而,as运营商进行不安全的演员和编译器警告一下:

Warning:(21, 16) Kotlin: Unchecked cast: kotlin.Int to T 
+0

感谢提供产生的2个DEX代码背后的细节出色的技术原因。这很好解释。我认为Kotlin语言应该改善它可以直接转换为java/lang/Number而不是java/lang/Long的地方。所以现在简而言之,他们是不同的。 Upvote你的答案并打勾接受它。谢谢!! – Elye

+0

你总是欢迎:) – soon

+0

你认为这是预期的行为? – voddan

1
return 1000 as T 

是一个未经检查的施放,编译器会发出关于它的警告。你总是返回一个Integer,但是你假装它是一个T,如果T实际上是Integer,那么它将工作得很好。在所有其他情况下,它会失败。

问题是明摆着会更加肆无忌惮,如果你选择了一个完全不相关的类型一样,我不知道,StringBuilder的:

var number: StringBuilder? = null 
val simpleObject = SimpleClass() 
number = simpleObject.getValue<StringBuilder>() 

现在你应该明白,这是没有意义的:你“再调用一个应该返回一个StringBuilder的方法,但它返回1000

相关问题