59 votes

Utilisation de traits Scala avec des méthodes implémentées en Java

J'imagine qu'il n'est pas possible d'invoquer des méthodes implémentées dans les traits Scala à partir de Java, ou existe-t-il un moyen?

Supposons que j'ai Scala:

 trait Trait {
  def bar = {}
}
 

et en Java si je l'utilise comme

 class Foo implements Trait {
}
 

Java se plaint que Trait is not abstract and does not override abstract method bar() in Trait

131voto

Tomasz Nurkiewicz Points 140462

Réponse

À partir de perspective Java Trait.scala est compilé en Trait interface. D'où la mise en œuvre de Trait en Java est interprété comme la mise en œuvre de l'interface qui rend vos messages d'erreur évidente. Réponse courte: vous ne pouvez pas profiter de trait implémentations en Java, car cela permettrait à l'héritage multiple en Java (!)

Comment est-il mis en œuvre dans Scala?

Réponse longue: alors, comment ça marche dans la Scala? En regardant le bytecode généré/classes, on peut trouver le code suivant:

interface Trait {
    void bar();
}

abstract class Trait$class {
    public static void bar(Trait thiz) {/*trait implementation*/}
}

class Foo implements Trait {
    public void bar() {
        Trait$class.bar(this);  //works because `this` implements Trait
    }
}
  • Trait est une interface
  • abstrait Trait$class (à ne pas confondre avec Trait.class), la classe est créée de manière transparente, qui, techniquement, ne pas mettre en oeuvre Trait interface. Il est cependant doté d'un static bar() méthode prenant Trait exemple comme argument (sorte de this)
  • Foo implémente Trait interface
  • scalac implémente automatiquement Trait méthodes en déléguant à la Trait$class. Cela signifie essentiellement appelant Trait$class.bar(this).

Notez que Trait$class n'est ni membre de l' Foo, ni n' Foo le prolonger. Il délègue simplement par le passage d' this.

Le mélange dans de multiples traits

Pour continuer la digression sur comment Scala fonctionne... cela étant dit, il est facile d'imaginer comment le mélange dans de multiples traits œuvres dessous:

trait Trait1 {def ping(){}};
trait Trait2 {def pong(){}};
class Foo extends Trait1 with Trait2

se traduit par:

class Foo implements Trait1, Trait2 {
  public void ping() {
    Trait1$class.ping(this);    //works because `this` implements Trait1
  }

  public void pong() {
    Trait2$class.pong(this);    //works because `this` implements Trait2
  }
}

Plusieurs traits de caractère primordial même méthode

Maintenant, il est facile d'imaginer comment le mélange de plusieurs traits de caractère primordial même méthode:

trait Trait {def bar(){}};
trait Trait1 extends Trait {override def bar(){}};
trait Trait2 extends Trait {override def bar(){}};

Nouveau Trait1 et Trait2 deviendront des interfaces d'étendre Trait. Maintenant, si Trait2 vient en dernier lors de la définition d' Foo:

class Foo extends Trait1 with Trait2

vous obtiendrez:

class Foo implements Trait1, Trait2 {
    public void bar() {
        Trait2$class.bar(this); //works because `this` implements Trait2
    }
}

Cependant commutation Trait1 et Trait2 (rendant Trait1 être le dernier):

class Foo implements Trait1, Trait2 {
    public void bar() {
        Trait1$class.bar(this); //works because `this` implements Trait1
    }
}

Empilable modifications

Considérons maintenant comment les traits de cumul des modifications de travail. Imaginez avoir vraiment utile en classe Foo:

class Foo {
  def bar = "Foo"
}

qui vous voulez enrichir avec de nouvelles fonctionnalités à l'aide de traits:

trait Trait1 extends Foo {
  abstract override def bar = super.bar + ", Trait1"
}

trait Trait2 extends Foo {
  abstract override def bar = super.bar + ", Trait2"
}

Voici le nouveau 'Foo' sur les stéroïdes:

class FooOnSteroids extends Foo with Trait1 with Trait2

Il se traduit par:

Trait1

interface Trait1 {
  String Trait1$$super$bar();
  String bar();
}
abstract class Trait1$class {
  public static String bar(Trait1 thiz) {
    // interface call Trait1$$super$bar() is possible
    // since FooOnSteroids implements Trait1 (see below)
    return thiz.Trait1$$super$bar() + ", Trait1";
  }
}

Trait2

public interface Trait2 {
  String Trait2$$super$bar();
  String bar();
}
public abstract class Trait2$class {
  public static String bar(Trait2 thiz) {
    // interface call Trait2$$super$bar() is possible
    // since FooOnSteroids implements Trait2 (see below)
    return thiz.Trait2$$super$bar() + ", Trait2";
  }
}

FooOnSteroids

class FooOnSteroids extends Foo implements Trait1, Trait2 {
  public final String Trait1$$super$bar() {
    // call superclass 'bar' method version
    return Foo.bar();
  }

  public final String Trait2$$super$bar() {
    return Trait1$class.bar(this);
  }

  public String bar() {
    return Trait2$class.bar(this);
  }      
}

Donc, l'ensemble de la pile invocations sont comme suit:

  • 'bar' méthode sur FooOnSteroids instance (point d'entrée);
  • Trait2$de la classe 'bar' méthode statique en la passant en argument et retournant une concaténation de " Trait2$$super$bar()' appel de la méthode et de la chaîne ", Trait2";
  • 'Trait2$$super$bar()' FooOnSteroids instance qui appelle ...
  • Trait1$de la classe 'bar' méthode statique en la passant en argument et retournant une concaténation de " Trait1$$super$bar()' appel de la méthode et de la chaîne ", Trait1";
  • 'Trait1$$super$bar" sur FooOnSteroids instance qui appelle ...
  • original Foo " bar " de la méthode

Et le résultat est "Foo, Trait1, Trait2".

Conclusion

Si vous avez réussi à tout lire, une réponse à la question d'origine est dans les quatre premières lignes...

2voto

paradigmatic Points 20871

Ce n'est en effet pas abstrait puisque bar renvoie un Unit vide (une sorte de NOP). Essayer:

 trait Trait {
  def bar: Unit
}
 

Alors bar sera une méthode abstraite Java renvoyant void .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X