2011-12-16 7 views
15

Si función acepta tipo estructural, que se puede definir como:¿Por qué Scala usa la reflexión para llamar al método en tipo estructural?

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

o

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

A continuación, puede utilizar esta función en la siguiente forma:

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

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

Si descompilar (a Java) las clases generadas por scalac para mi ejemplo, puede ver que el argumento de doTheThings es del tipo Object y el im La plementación utiliza la reflexión para llamar a métodos en el argumento (es decir duck.quack)

Mi pregunta es ¿por qué reflexión? ¿No es posible usar anonymous e invokevirtual en lugar de reflection?

Aquí es manera de traducir (implementar) el tipo estructural llama para mi ejemplo (sintaxis de Java, pero el punto es el código de bytes):

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

Respuesta

13

Considere una simple proposición:

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 

Imprimirá Different ducks según su propuesta. Podrías refinarlo aún más, pero simplemente no puedes mantener la igualdad referencial intacta usando un proxy.

Una posible solución sería usar el patrón de clase de tipo, pero eso requeriría pasar otro parámetro (incluso si está implícito). Aún así, es más rápido. Pero eso se debe principalmente a la cojera de la velocidad de reflexión de Java. Con suerte, los identificadores de método sortearán el problema de la velocidad. Desafortunadamente, Scala no está programado para renunciar a Java 5, 6 y 7 (que no tienen controladores de método) por algún tiempo ...

+0

No recibo la última oración, ¿pueden explicarme? –

+0

@ om-nom-nom 'invokevirtual' no está presente en JVM 1.5 y 1.6, por lo que Scala no puede confiar en él. Scala 2.10 realmente depreciará JVM 1.5, pero aún le queda algo de tiempo antes de que Scala pueda aprovechar lo presente solo en JVM 1.7. –

+0

@Daniel C. Sobral: Supongo que querías 'invookedynamic' en lugar de' invokevirtual' en tu último comentario –

10

Además de los métodos de implementación del objeto proxy en el tipo estructural, también necesita tener implementaciones de paso apropiadas de todos los métodos en Cualquiera (igual, hashCode, toString, isInstanceOf, asInstanceOf) y AnyRef (getClass, wait, notify, notifyAll y synchronized). Si bien algunos de estos serían simples, algunos serían casi imposibles de corregir. En particular, todos los métodos enumerados son "finales" en AnyRef (para la compatibilidad y seguridad de Java) y, por lo tanto, su objeto proxy no pudo implementarlos correctamente.

+1

@Daniel C.Sobral y Dave Griffith: Sus dos respuestas son aceptables. Así que tuve que arrojar una moneda para aceptarla formalmente. –

Cuestiones relacionadas