2011-12-16 11 views
15

Se funzione accetta tipo strutturale, può essere definito come:Perché scala usa il reflection per chiamare il metodo sul tipo strutturale?

def doTheThings(duck: { def walk; def quack }) { duck.quack } 

o

type DuckType = { def walk; def quack } 
def doTheThings(duck: DuckType) { duck.quack } 

Quindi, è possibile utilizzare tale funzione nel modo seguente:

class Dog { 
    def walk { println("Dog walk") } 
    def quack { println("Dog quacks") } 
} 

def main(args: Array[String]) { 
    doTheThings(new Dog); 
} 

Se si decompila (in Java) le classi generate da scalac per il mio esempio, puoi vedere che l'argomento di doTheThings è di tipo Object e l'im la plementazione usa la riflessione per chiamare i metodi sull'argomento (ad es. duck.quack)

La mia domanda è perché la riflessione? Non è possibile usare solo l'anonimo e invokevirtual invece di riflessione?

Qui è il modo di tradurre (implementare) il tipo strutturale richiede per il mio esempio (sintassi di Java, ma il punto è il bytecode):

class DuckyDogTest { 
    interface DuckType { 
    void walk(); 
    void quack(); 
    } 

    static void doTheThing(DuckType d) { 
    d.quack(); 
    } 

    static class Dog { 
    public void walk() { System.out.println("Dog walk"); } 
    public void quack() { System.out.println("Dog quack"); } 
    } 

    public static void main(String[] args) { 
    final Dog d = new Dog(); 
    doTheThing(new DuckType() { 
     public final void walk() { d.walk(); } 
     public final void quack() { d.quack();} 
    }); 
    } 
} 

risposta

13

Si consideri una semplice proposizione:

type T = { def quack(): Unit; def walk(): Unit } 
def f(a: T, b: T) = 
    if (a eq b) println("They are the same duck!") 
    else  println("Different ducks") 

f(x, x) // x is a duck 

Si stampa Different ducks sotto la vostra proposta. Potresti perfezionarlo ulteriormente, ma non puoi mantenere intatto l'uguaglianza referenziale usando un proxy.

Una possibile soluzione sarebbe utilizzare il modello di classe del tipo, ma ciò richiederebbe il passaggio di un altro parametro (anche se implicito). Eppure, è più veloce. Ma questo è principalmente a causa della zoppia della velocità di riflessione di Java. Si spera che le maniglie del metodo risolveranno il problema della velocità. Sfortunatamente a Scala non è programmato di rinunciare a Java 5, 6 e 7 (che non hanno maniglie di metodo) per un po 'di tempo ...

+0

Non capisco l'ultima frase, puoi, per favore, spiegare? –

+0

@ om-nom-nom 'invokevirtual' non è presente su JVM 1.5 e 1.6, quindi Scala non può fare affidamento su di esso. Scala 2.10 in realtà deprecare JVM 1.5, ma è ancora un po 'di tempo prima che Scala possa sfruttare le cose presenti solo su JVM 1.7. –

+0

@Daniel C. Sobral: Immagino che intendessi "invokedynamic" invece di "invokevirtual" nel tuo ultimo commento –

10

In aggiunta ai metodi di implementazione dell'oggetto proxy sul tipo strutturale, sarebbe anche è necessario disporre di implementazioni pass-through appropriate di tutti i metodi su Any (uguale a hashCode, toString, isInstanceOf, asInstanceOf) e AnyRef (getClass, wait, notify, notifyAll e synchronized). Mentre alcuni di questi sarebbero diretti, alcuni sarebbero quasi impossibili da ottenere. In particolare, tutti i metodi elencati sono "final" su AnyRef (per compatibilità e sicurezza Java) e quindi non possono essere implementati correttamente dal tuo oggetto proxy.

+1

@Daniel C.Sobral e Dave Griffith: entrambe le tue risposte sono accettabili. Quindi ho dovuto gettare una moneta per accettarne formalmente una. –

Problemi correlati