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...