2010-08-05 12 views
46

ho visto che ci sono due metodi per lanciare un oggetto in Scala:Quali sono le differenze tra asInstanceOf [T] e (o: T) in Scala?

foo.asInstanceOf[Bar] 
(foo: Bar) 

Quando ho provato, ho scoperto che asInstanceOf non usa conversione implicita mentre l'altro lo fa.

Quali sono le differenze di comportamento tra questi due metodi? E dove si consiglia di usarne uno sull'altro?

risposta

78
  • foo.asInstanceOf[Bar] è un tipo getto, che è principalmente un'operazione di runtime. Dice che il compilatore dovrebbe essere forzato a credere che foo è un Bar. Ciò potrebbe causare un errore (a ClassCastException) se e quando foo viene valutato come diverso da Bar in fase di esecuzione.

  • foo:Bar è un tipo ascription, che è interamente un'operazione di compilazione. Ciò fornisce al compilatore assistenza per comprendere il significato del tuo codice, senza costringerlo a credere a nulla che possa essere falso; nessun errore di runtime può derivare dall'uso di attribuzioni di tipo.

Le attribuzioni di tipo possono essere utilizzate anche per attivare conversioni implicite. Ad esempio, è possibile definire la seguente conversione implicita:

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

e quindi garantire il suo uso in questo modo:

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

attribuire tipo Int ad un String sarebbe normalmente un errore di tipo compilazione, ma prima di rinunciare al compilatore cercherà le conversioni implicite disponibili per far sparire il problema. La particolare conversione implicita che verrà utilizzata in un determinato contesto è nota al momento della compilazione.

Inutile dire che gli errori di runtime sono indesiderabili, quindi la misura in cui è possibile specificare le cose in una manieratype-safe (senza utilizzare asInstanceof), meglio è!Se ti ritrovi a usare asInstanceOf, probabilmente dovresti usare lo match.

+2

Un caso dove tipo annotazione è necessario è quando si desidera specificare il tipo per null, ad es quando si utilizzano le API Java. – Landei

+0

Grazie per questa grande spiegazione! –

+0

@pelotom: Hey il tuo esempio è fantastico! "ciao": Int ... Non ho mai visto o usato impliciti in quel modo ma veramente una cosa interessante. – soc

8

Programmazione in Scala si occupa di questo in un po 'di dettagli in Capitolo 15 - Classi case e Pattern Matching.

Fondamentalmente il secondo modulo può essere utilizzato come 'Modello digitato' in una corrispondenza di modello, dando la funzionalità isInstanceOf e asInstanceOf. Confronta

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

vs.

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

Gli autori suggeriscono che la verbosità del isInstance* modo di fare le cose è voluto spingere voi nello stile pattern matching.

Non sono sicuro quale modello sia più efficace per un cast di tipo semplice senza un test.

16

risposta di Pelotom copre la teoria abbastanza piacevole, ecco alcuni esempi per rendere più chiaro:

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 
} 

Come si può vedere il tipo di attribuzione può essere utilizzato per risolvere disambiguazioni. A volte possono essere irrisolvibili dal compilatore (vedere più avanti), che segnalerà un errore e sarà necessario risolverlo. In altri casi (come nell'esempio) usa solo il metodo "sbagliato", non quello che vuoi. foo(a: String) non viene compilato, a indicare che l'attribuzione del tipo non è un cast. Confrontalo con la riga precedente, dove il compilatore è felice, ma ottieni un'eccezione, quindi l'errore viene rilevato con l'attribuzione del tipo.

Si otterrà un'ambiguità non risolvibile se anche si aggiunge un metodo di

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

In questo caso la prima e la terza invocazione di foo non verrà compilato, come il compilatore non può decidere se si desidera chiamare il foo con un singolo parametro Any, o con i varargs, dato che entrambi sembrano ugualmente buoni => devi usare un attribtion di tipo per aiutare il compilatore.

Modifica puoi anche What is the purpose of type ascription in Scala?

+0

+1 esempi concreti –

2

C'è esempio della differenza:

  1. Tipo cast (asInstanceOf) è un'operazione di run-time con il runtime potenzialmente eccezione.
  2. Ascription è fondamentalmente solo un up-cast eseguito in fase di compilazione.

Esempio:

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 

Possiamo anche chiamare il metodo utilizzando multipla spedizione:

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" 

Possiamo definire conversione implicita:

// 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) 
Problemi correlati