2010-08-05 6 views
46

ich sah, dass es zwei Methoden, um ein Objekt in Scala zu gießen:Was sind die Unterschiede zwischen asInstanceOf [T] und (o: T) in Scala?

foo.asInstanceOf[Bar] 
(foo: Bar) 

Als ich versuchte, fand ich, dass asInstanceOf nicht implizite Konvertierung nicht verwendet, während der andere der Fall ist.

Was sind die Unterschiede im Verhalten zwischen diesen beiden Methoden? Und wo wird empfohlen, übereinander zu verwenden?

Antwort

78
  • foo.asInstanceOf[Bar] ist eine Art Guss, die in erster Linie ein Laufzeit-Betrieb ist. Es besagt, dass der Compiler gezwungen werden sollte zu glauben, dass foo ein Bar ist. Dies kann zu einem Fehler führen (a ClassCastException), wenn foo zur Laufzeit als etwas anderes als Bar ausgewertet wird.

  • foo:Bar ist ein Typ Zuschreibung, die vollständig eine Kompilierzeitoperation ist. Dadurch erhält der Compiler Unterstützung beim Verständnis der Bedeutung Ihres Codes, ohne dass er etwas glauben muss, das möglicherweise falsch ist. Aus der Verwendung von Typ-Zuordnungen können keine Laufzeitfehler resultieren.

Typzuschreibungen können auch zum Auslösen impliziter Konvertierungen verwendet werden. Zum Beispiel könnten Sie die folgende implizite Konvertierung definieren:

implicit def foo(s:String):Int = s.length 

und dann sicherzustellen, seine Verwendung in etwa so:

scala> "hi":Int         
res29: Int = 2 

zuschreibt Typ Int zu einem String würde normalerweise ein Compiler-Typ Fehler sein, aber Bevor der Compiler aufgibt, sucht er nach verfügbaren impliziten Konvertierungen, um das Problem zu beheben. Die bestimmte implizite Konvertierung, die in einem bestimmten Kontext verwendet wird, ist zur Kompilierungszeit bekannt.

Unnötig zu sagen, sind Laufzeitfehler unerwünscht, so dass das Ausmaß, in dem Sie die Dinge in eine typsichere Weise (ohne asInstanceof verwenden) angeben können, desto besser!Wenn Sie sich selbst mit asInstanceOf finden, sollten Sie stattdessen match verwenden.

+2

Ein Fall, in dem Typanmerkung ist benötigt wird, wenn Sie den Typ für null angeben möchten, z bei Verwendung von Java-APIs. – Landei

+0

Danke für diese großartige Erklärung! –

+0

@pelotom: Hey, dein Beispiel ist großartig! "hi": Int ... Ich habe niemals implicits auf diese Weise gesehen oder benutzt, aber wirklich eine interessante Sache. – soc

8

Programmierung in Scala deckt diese in ein wenig Detail in Kapitel 15 - Case Klassen und Muster Passende.

Grundsätzlich kann das zweite Formular als "typisiertes Muster" in einer Musterübereinstimmung verwendet werden, was die Funktionen isInstanceOf und asInstanceOf ermöglicht. Vergleichen

if (x.isInstanceOf[String]) { 
    val s = x.asInstanceOf[String] 
    s.length 
} else ... 

gegen

def checkFoo(x: Any) = x match { 
    case s: String => s.length 
    case m: Int => m 
    case _ => 0 
} 

Die Autoren deuten, dass die Ausführlichkeit der isInstance* Art und Weise, Dinge zu tun beabsichtigt ist Sie in den Pattern-Matching-Stil schubsen.

Ich bin nicht sicher, welches Muster für einen einfachen Typ Cast ohne einen Test effektiver ist.

16

Pelotom Antwort deckt die Theorie sehr schön, hier sind einige Beispiele deutlicher zu machen:

def foo(x: Any) { 
    println("any") 
} 

def foo(x: String) { 
    println("string") 
} 


def main(args: Array[String]) { 
    val a: Any = new Object 
    val s = "string" 

    foo(a)      // any 
    foo(s)      // string 
    foo(s: Any)     // any 
    foo(a.asInstanceOf[String]) // compiles, but ClassCastException during runtime 
    foo(a: String)    // does not compile, type mismatch 
} 

Wie Sie der Typ Zuschreibung sehen kann, kann verwendet werden Begriffsklärung zu lösen. Manchmal können sie vom Compiler nicht aufgelöst werden (siehe später), was einen Fehler meldet und Sie müssen ihn beheben. In anderen Fällen (wie im Beispiel) wird nur die "falsche" Methode verwendet, nicht die gewünschte. foo(a: String) kompiliert nicht und zeigt, dass die Typzuweisung keine Besetzung ist. Vergleichen Sie es mit der vorherigen Zeile, wo der Compiler glücklich ist, aber Sie erhalten eine Ausnahme, so dass der Fehler dann mit der Typzuschreibung erkannt wird.

Sie eine nicht auflösbare Mehrdeutigkeit, wenn man auch ein Verfahren

def foo(xs: Any*) { 
    println("vararg") 
} 

In diesem Fall ist der erste und die dritte Aufruf von foo kompiliert nicht hinzufügen, da der Compiler nicht entscheiden können, ob Sie die foo anrufen mögen mit einem einzigen Any-Parameter oder mit den Varargs, da beide gleich gut zu sein scheinen => Sie müssen eine Typ-Beschreibung verwenden, um dem Compiler zu helfen.

bearbeiten siehe auch What is the purpose of type ascription in Scala?

+0

+1 schöne konkrete Beispiele –

2

Es ist Beispiel für den Unterschied:

  1. Typ Besetzung (asInstanceOf) ist ein Laufzeit-Betrieb mit möglicherweise Laufzeit Ausnahme.
  2. Ascription ist im Grunde nur eine Up-Cast zur Kompilierzeit durchgeführt.

Beispiel:

class Parent() { def method() {} } 
class Child1 extends Parent() { def method1() {} } 
class Child2 extends Parent() { def method2() {} } 

// we return Parent type 
def getChild1() : Parent = new Child1() 
def getChild2() : Parent = new Child2() 
def getChild() : Child1 = new Child1() 

(getChild1().asInstanceOf[Child1]).method1() // OK 
(getChild1().asInstanceOf[Child2]).method2() // runtime ClassCastException 

(getChild1() : Child2).method2() // compile-time error 
(getChild2() : Child2).method2() // compile-time error 
(getChild() : Parent).method1() // compile-time error 
(getChild()).method() // OK 

// with asInstanceOf, we can cast to anything without compile-time error 
getChild1().asInstanceOf[String] // runtime ClassCastException 
getChild1().asInstanceOf[Int] // runtime ClassCastException 

wir auch Verfahren unter Verwendung von Multiple-Dispatch-Anruf kann:

def prt(p: Parent) = println("parent") 
def prt(ch: Child1) = println("child") 

prt(new Parent()) // prints "parent" 
prt((new Child1()) : Parent) // prints "parent" 
prt(new Child1()) // prints "child" 

prt(new Parent().asInstanceOf[Child1]) // runtime ClassCastException 
prt(new Child1().asInstanceOf[Parent]) // prints "parent" 

Wir können implizite Konvertierung definieren:

// after definition of implicit conversions 
implicit def toChild1(p: Parent) : Child1 = new Child1() 
implicit def toChild2(p: Parent) : Child2 = new Child2() 

(getChild1() : Child2).method2() // OK - implicit conversion to Child2 in ascription 
(getChild2() : Child2).method2() // OK - implicit conversion to Child2 in ascription 
(getChild2()).method1() // OK - implicit conversion to Child1 when calling method1() 
(getChild2()).method2() // OK - implicit conversion to Child2 when calling method2() 
(getChild2() : Parent).method() // OK - no implicit conversion 
(getChild() : Parent).method1() // OK - implicit conversion to Child1 when calling method() 

getChild1().asInstanceOf[Int] // still runtime ClassCastException (no implicit conversion) 
Verwandte Themen