2015-12-10 22 views
8

Ich habe versucht, this Frage zu beantworten, da ich dachte, ich wüsste die Antwort. Es stellte sich heraus, ich habe nicht genug wissen:/Warum wirft `.asInstanceOf 'manchmal und manchmal nicht?

Hier ist ein Test, den ich getan habe:

class Inst[T] { 
    def is(x: Any) = scala.util.Try { as(x) }.isSuccess 
    def as(x: Any): T = x.asInstanceOf[T] 
} 

scala> new Inst[String].is(3) 
res17: Boolean = true 

scala> new Inst[String].as(3) 
java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String 
... 33 elided 

Was ist hier los? Warum wirft nur der zweite Anruf as, aber nicht den ersten?

Antwort

10

Dies ist, weil die Klasse-Cast-Ausnahme nur ausgelöst wird, wenn Sie etwas mit dem Wert tun, rufen Sie eine Methode nach der Besetzung auf. In der REPL zum Beispiel würden Sie im zweiten Fall einen toString Aufruf haben. Hinweis:

new Inst[String].as(3);()   // nothing happens 
new Inst[String].as(3).toString;() // exception 

Der Grund, warum dies der Extraschritt dauert ist, dass Inst[T] mit Typ-Parameter T generisch ist, die zur Laufzeit gelöscht wird; Nur wenn die Call-Site (die über ein statisches Wissen vom Typ T verfügt) versucht, eine Methode für das Ergebnis aufzurufen, wird die tatsächliche Typüberprüfung durchgeführt.


Für Ihre weitere Frage ist toString für jedes Objekt definiert und da T generisch haben Sie eine Box-integer (<: AnyRef) und toString und println im is Methode erfolgreich zu sein. So ein anderes Beispiel, bei dem die Try fehlschlagen würde, ist dies:

class Inst[T] { 
    def foo(x: Any)(implicit ev: T <:< String) = scala.util.Try { 
    ev(as(x)).reverse 
    }.isSuccess 
} 

new Inst[String].foo(3) // false! 
+0

Nein, das scheint nicht viel zu erklären: Ich habe die 'is' Definition geändert in: 'def ist (x: Any) = scala.util.Try {als (x) .toString} .isSuccess', und es gibt immer noch' true' (dh die Wurf wirft nicht). Sogar dieses 'def ist (x: Any) = scala.util.Try {println (als (x) .toString)} .isSuccess;' gibt glücklich "3" aus und gibt true zurück: -/ – Dima

+0

Bitte sehen meine edit –

+0

Ah, es macht jetzt Sinn, danke! 'is' weiß nicht, was' T' ist, also behandelt es das Argument als 'Any'. Ich habe es versucht: 'trait Foo {def foo = ??? } Klasse Inst [T <: Foo] {def ist (x: Beliebig) = scala.util.Try {as (x) .foo} .isSuccess; def as (x: beliebig): T = x.asInstanzOf [T]; } '. Nun gibt 'new Inst [Foo] .is (3)' wie erwartet "false" zurück. – Dima

2

Während @ 0 __ 's Antwort erklärt, warum es nicht funktioniert, ist hier, wie es funktioniert:

class Inst[T](implicit tag: scala.reflect.ClassTag[T]) { 
    def is(x: Any) = tag.runtimeClass.isInstance(x) 
    // scala.util.Try { as(x) }.isSuccess will work as well 
    def as(x: Any): T = tag.runtimeClass.cast(x).asInstanceOf[T] 
} 

object Main extends App { 
    println(new Inst[String].is(3)) 
    println(new Inst[String].as(3)) 
} 


false 
java.lang.ClassCastException: Cannot cast java.lang.Integer to java.lang.String 
    at java.lang.Class.cast(Class.java:3369) 
... 
Verwandte Themen