2011-12-16 2 views
15

Si la fonction accepte le type de structure, il peut être défini comme:Pourquoi scala utilise la réflexion pour appeler la méthode sur le type structurel?

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

ou

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

, vous pouvez utiliser cette fonction comme suit:

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

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

Si vous décompiler (à Java) les classes générées par scalac pour mon exemple, vous pouvez voir que l'argument de doTheThings est de type Object et l'im La mise en œuvre utilise la réflexion pour appeler les méthodes sur l'argument (c.-à-d. duck.quack)

Ma question est pourquoi réflexion? N'est-il pas possible d'utiliser anonyme et invokevirtual au lieu de la réflexion?

Voici façon de traduire (mise en œuvre) le type de structure appelle pour mon exemple (syntaxe Java, mais le point est le 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();} 
    }); 
    } 
} 

Répondre

13

Considérons une proposition simple:

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 

Il faudrait imprimer Different ducks sous votre proposition. Vous pouvez l'affiner davantage, mais vous ne pouvez pas conserver l'égalité référentielle intacte en utilisant un proxy.

Une solution possible serait d'utiliser le modèle de classe de type, mais cela nécessiterait de passer un autre paramètre (même s'il est implicite). Pourtant, c'est plus rapide. Mais c'est principalement à cause de la boiterie de la vitesse de réflexion de Java. Heureusement, les poignées de méthode contourneront le problème de vitesse. Malheureusement, Scala n'est pas programmé pour abandonner sur Java 5, 6 et 7 (qui n'ont pas de descripteurs de méthode) pendant un certain temps ...

+0

Je ne comprends pas la dernière phrase, pouvez-vous, s'il vous plaît, expliquer? –

+0

@ om-nom-nom 'invokevirtual' n'est pas présent sur JVM 1.5 et 1.6, donc Scala ne peut pas compter dessus. Scala 2.10 va réellement déprécier JVM 1.5, mais il faudra encore du temps avant que Scala ne profite des choses présentes uniquement sur JVM 1.7. –

+0

@Daniel C. Sobral: Je suppose que vous vouliez dire 'invokedynamic' au lieu de' invokevirtual' dans votre dernier commentaire –

10

En plus de vos méthodes d'implémentation d'objet proxy sur le type structurel, il serait également doivent avoir des implémentations pass-through appropriées de toutes les méthodes sur Any (equals, hashCode, toString, isInstanceOf, asInstanceOf) et AnyRef (getClass, wait, notify, notifyAll et synchronized). Alors que certains d'entre eux seraient simples, certains seraient presque impossibles à corriger. En particulier, toutes les méthodes listées sont "finales" sur AnyRef (pour la compatibilité et la sécurité de Java) et n'ont donc pas pu être implémentées correctement par votre objet proxy.

+1

@Daniel C.Sobral et Dave Griffith: Vos deux réponses sont acceptables. J'ai donc dû lancer une pièce pour en accepter officiellement une. –

Questions connexes