2011-12-16 8 views
15

Jeśli funkcja przyjmuje typ strukturalny, może być zdefiniowane jako:Dlaczego scala używa refleksji do wywołania metody na typ strukturalny?

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

lub

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

Następnie można użyć tej funkcji w następujący sposób:

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

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

Jeśli dekompilować (do Java) klas generowanych przez skalak dla mojego przykładu, widać, że argument doTheThings jest typu Object, a im plementacja używa refleksji do wywoływania metod na argumentach (tj. duck.quack)

Moje pytanie brzmi: dlaczego refleksja? Czy nie można po prostu użyć anonimowego i invokevirtual zamiast odbicia?

Oto sposób tłumaczyć (wdrożenie) typ strukturalny wzywa na moim przykładzie (składni Javy, ale punkt jest kod bajtowy):

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();} 
    }); 
    } 
} 

Odpowiedz

13

Rozważmy prostą propozycję:

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 

Zostanie wydrukowany pod Twoją propozycją Different ducks. Możesz go jeszcze udoskonalić, ale nie możesz po prostu zachować referencyjnej równości za pomocą proxy.

Możliwe rozwiązanie polegałoby na użyciu wzorca klasy typów, ale wymagałoby to podania innego parametru (nawet jeśli niejawny). Mimo to jest szybszy. Ale to głównie z powodu kulawości prędkości refleksji Javy. Mamy nadzieję, że uchwyty metod omijają problem prędkości. Niestety, Scala nie jest planowana do rezygnacji z Java 5, 6 i 7 (które nie mają uchwytów metod) przez jakiś czas ...

+0

Nie dostaję ostatniego zdania, czy możesz, proszę, wyjaśnić? –

+0

@ om-nom-nom 'invokevirtual' nie występuje w JVM 1.5 i 1.6, więc Scala nie może na nim polegać. Scala 2.10 faktycznie wycofa JVM 1.5, ale to jeszcze trochę czasu, zanim Scala będzie mogła skorzystać z rzeczy obecnych tylko na JVM 1.7. –

+0

@ Daniel C. Sobral: Myślę, że miałeś na myśli 'invokedynamic' zamiast' invokevirtual' w twoim ostatnim komentarzu –

10

Oprócz twoich metod realizacji obiektu proxy na typie konstrukcji, byłaby to również potrzebujemy odpowiednich implementacji tranzytu wszystkich metod Any (równy, hashCode, toString, isInstanceOf, asInstanceOf) i AnyRef (getClass, wait, notify, notifyAll i synchronized). Podczas gdy niektóre z nich byłyby proste, niektóre byłyby prawie niemożliwe do uzyskania. W szczególności, wszystkie wymienione metody są "ostateczne" w AnyRef (dla zgodności i bezpieczeństwa Java), a zatem nie mogą być poprawnie implementowane przez obiekt proxy.

+1

@Daniel C.Sobral i Dave Griffith: Obie twoje odpowiedzi są do przyjęcia. Musiałem więc rzucić monetą, aby formalnie ją przyjąć. –

Powiązane problemy