Je suppose 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’aie en 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
Du sharepoint vue Java, Trait.scala
est compilé dans l’ interface Trait
. Ainsi, implémenter Trait
dans Java est interprété comme implémentant une interface – ce qui rend vos messages d’erreur évidents. Réponse courte: vous ne pouvez pas tirer parti des implémentations de traits en Java, car cela permettrait l’inheritance multiple en Java (!)
Réponse longue: comment ça marche à Scala? En regardant le bytecode / classes généré, 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 Trait$class
abstract Trait$class
(ne pas confondre avec Trait.class
) est créée de manière transparente, ce qui, techniquement, Trait.class
pas l’ interface Trait
. Cependant, il a une méthode de static bar()
prenant l’instance Trait
en argument (en quelque sorte) Foo
implémente l’interface Trait
scalac
implémente automatiquement les méthodes Trait
en déléguant à la Trait$class
. Cela signifie essentiellement appeler Trait$class.bar(this)
. Notez que Trait$class
n’est ni membre de Foo
, ni Foo
étend. Il y délègue simplement en passant this
.
Poursuivre la digression sur le fonctionnement de Scala … Cela étant dit, il est facile d’imaginer comment le mélange de traits multiples fonctionne en 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 } }
Maintenant, il est facile d’imaginer comment le mélange de plusieurs caractères se substituant à la même méthode:
trait Trait {def bar(){}}; trait Trait1 extends Trait {override def bar(){}}; trait Trait2 extends Trait {override def bar(){}};
Là encore, Trait1
et Trait2
deviendront des interfaces d’extension de Trait
. Maintenant, si Trait2
arrive en dernier en définissant Foo
:
class Foo extends Trait1 with Trait2
tu auras:
class Foo implements Trait1, Trait2 { public void bar() { Trait2$class.bar(this); //works because `this` implements Trait2 } }
Cependant, si vous Trait1
et Trait2
(rendant Trait1
en dernier), vous obtiendrez:
class Foo implements Trait2, Trait1 { public void bar() { Trait1$class.bar(this); //works because `this` implements Trait1 } }
Considérons maintenant comment fonctionnent les modifications empilables. Imaginez avoir une classe vraiment utile Foo:
class Foo { def bar = "Foo" }
que vous souhaitez enrichir de nouvelles fonctionnalités en utilisant des 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
Cela se traduit par:
interface Trait1 { Ssortingng Trait1$$super$bar(); Ssortingng bar(); } abstract class Trait1$class { public static Ssortingng bar(Trait1 thiz) { // interface call Trait1$$super$bar() is possible // since FooOnSteroids implements Trait1 (see below) return thiz.Trait1$$super$bar() + ", Trait1"; } }
public interface Trait2 { Ssortingng Trait2$$super$bar(); Ssortingng bar(); } public abstract class Trait2$class { public static Ssortingng bar(Trait2 thiz) { // interface call Trait2$$super$bar() is possible // since FooOnSteroids implements Trait2 (see below) return thiz.Trait2$$super$bar() + ", Trait2"; } }
class FooOnSteroids extends Foo implements Trait1, Trait2 { public final Ssortingng Trait1$$super$bar() { // call superclass 'bar' method version return Foo.bar(); } public final Ssortingng Trait2$$super$bar() { return Trait1$class.bar(this); } public Ssortingng bar() { return Trait2$class.bar(this); } }
Les invocations de la stack sont donc les suivantes:
Et le résultat est “Foo, Trait1, Trait2”.
Si vous avez tout lu, une réponse à la question initiale se trouve dans les quatre premières lignes …
En effet, ce n’est pas abstrait puisque la bar
retourne une Unit
vide (une sorte de NOP). Essayer:
trait Trait { def bar: Unit }
Ensuite, la bar
sera une méthode abstraite Java renvoyant le void
.