2016-07-01 11 views
4

Wenn ich die folgende haben, es funktioniert (dh Nummer 1000 erhalten zuweisen)java.lang.Integer kann nicht auf java.lang.Long in Kotlin gegossen werden (wenn der Anfangswert null ist)

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

Wenn ich habe die folgende, es funktioniert

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

(dh Nummer 1000 erhalten zuweisen) Aber wenn ich die unten haben, versagt es

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

der Fehler gemeldet ist auf der number = simpleObject.getValue<Long>() Linie

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

Warum, wenn ich var number: Long ? = null und var number: Long = 0 anderes Ergebnis haben initialisieren? Habe ich etwas falsch gemacht?

AKTUALISIERT

Eine Abhilfe die unten verwenden, ist das Ergebnis in Ordnung. Es wird jedoch eine zusätzliche temporäre Variable verwendet.

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

Antwort

4

einen Blick in die erzeugten Bytecode nehmen lassen:

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 

Wie Sie können sich e, ist diese Methode nicht nicht gegossen 1000 an ein Long es einfach gewährleistet, dass das Objekt vom Typ java/lang/Object (gut, es ist) und gibt die 1000 als Integer Objekt.

Daher können Sie diese Methode mit einem beliebigen Typ aufrufen (Anmerkung: nur aufrufen) und dies wird keine Ausnahme auslösen. Allerdings ruft echte Besetzung in einer Variablen das Ergebnis zu speichern, die zu 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 
} 

Aber warum Speichern des Ergebnisses führen kann als eine Long? eine Ausnahme auslöst?Auch hier wollen wir uns die Unterschiede in Bytecode einen Blick:

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 

Wie Sie der Bytecode für number: Long wirft sehen können, ruft die Funktion Ergebnis auf eine Number und dann Number.longValue, um den Wert auf ein Long (long zu konvertieren in Java)

die Funktion Ergebnis der Bytecode für number: Long? wirft jedoch direkt in die Long? (Long in Java), die führt zu ClassCastException.

Nicht sicher, ob dieses Verhalten irgendwo dokumentiert ist. Jedoch führt der as Operator unsichere Besetzung und der Compiler warnt darüber:

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

Dank für die exzellente Detail technischen Grund für die 2 dex-Codes erzeugt bereitstellt. Das erklärt es gut. Ich denke, dass die Kotlin-Sprache verbessert werden sollte, wo sie direkt in java/lang/Number anstelle von java/lang/Long umgewandelt werden könnte. Kurzum, sie sind einfach anders. Upvote deine Antwort und kreuze an, um es zu akzeptieren. Vielen Dank!! – Elye

+0

Sie sind immer willkommen :) – soon

+0

Denken Sie, dass dies das beabsichtigte Verhalten ist? – voddan

1
return 1000 as T 

ist eine ungeprüfte gegossen, und der Compiler gibt eine Warnung darüber. Du gibst immer einen Integer zurück, aber du tust so, als wäre es ein T. Das funktioniert nur, wenn T tatsächlich Integer ist. In allen anderen Fällen wird es fehlschlagen.

Die Offensichtlichkeit des Problems wäre krass, wenn Sie ein völlig anderer Typ wie gewählt hat, weiß ich nicht, String:

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

Jetzt sollten Sie erkennen, dass dies Sinn macht, nicht: Sie bist ein Aufruf der Methode, die angeblich ein String zurück, aber es gibt 1000.