Ответ
С точки зрения Java Trait.scala
компилируется в Trait
интерфейс . Следовательно, реализация Trait
в Java интерпретируется как реализация интерфейса, что делает ваши сообщения об ошибках очевидными. Короткий ответ: вы не можете воспользоваться преимуществами реализации признаков в Java, потому что это позволило бы множественное наследование в Java (!)
Как это реализовано в Scala?
Длинный ответ: так как это работает в Scala? Глядя на сгенерированный байт-код / классы, можно найти следующий код:
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
это интерфейс
- abstract
Trait$class
(не путайте с Trait.class
) класс создается прозрачно, что технически не реализует интерфейс Trait
. Однако у него есть static bar()
метод, принимающий Trait
экземпляр в качестве аргумента (вроде this
)
Foo
реализует Trait
интерфейс
scalac
автоматически реализует Trait
методы путем делегирования Trait$class
. По сути, это означает, что звонить Trait$class.bar(this)
.
Обратите внимание, что Trait$class
не является ни членом Foo
, ни Foo
не расширяет его. Он просто делегирует ему, передавая this
.
Смешивание в нескольких чертах
Продолжая отступление о том, как работает Scala ... При этом легко представить, как работает смешение в нескольких чертах:
trait Trait1 {def ping(){}};
trait Trait2 {def pong(){}};
class Foo extends Trait1 with Trait2
переводится как:
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
}
}
Несколько черт, перекрывающих один и тот же метод
Теперь легко представить, как смешивание в нескольких чертах перекрывает один и тот же метод:
trait Trait {def bar(){}};
trait Trait1 extends Trait {override def bar(){}};
trait Trait2 extends Trait {override def bar(){}};
Снова Trait1
и Trait2
станут интерфейсами, расширяющими Trait
. Теперь, если Trait2
стоит последним при определении Foo
:
class Foo extends Trait1 with Trait2
вы получите:
class Foo implements Trait1, Trait2 {
public void bar() {
Trait2$class.bar(this); //works because `this` implements Trait2
}
}
Однако переключение Trait1
и Trait2
(делая Trait1
последним) приведет к:
class Foo implements Trait2, Trait1 {
public void bar() {
Trait1$class.bar(this); //works because `this` implements Trait1
}
}
Стекируемые модификации
Теперь рассмотрим, как работают черты как наращиваемые модификации. Представьте себе действительно полезный класс Foo:
class Foo {
def bar = "Foo"
}
, который вы хотите обогатить некоторыми новыми функциями, используя черты:
trait Trait1 extends Foo {
abstract override def bar = super.bar + ", Trait1"
}
trait Trait2 extends Foo {
abstract override def bar = super.bar + ", Trait2"
}
Вот новый 'Foo' на стероидах:
class FooOnSteroids extends Foo with Trait1 with Trait2
Это переводится как:
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);
}
}
Итак, весь вызов стека выглядит следующим образом:
- метод 'bar' в экземпляре FooOnSteroids (точка входа);
- Статический метод 'bar' класса Trait2 $, передающий его в качестве аргумента и возвращающий объединение вызова метода Trait2 $$ super $ bar () и строки ", Trait2";
- 'Trait2 $$ super $ bar ()' в экземпляре FooOnSteroids, который вызывает ...
- Статический метод 'bar' класса Trait1 $, передающий его в качестве аргумента и возвращающий объединение вызова метода Trait1 $$ super $ bar () и строки ", Trait1";
- 'Trait1 $$ super $ bar' в экземпляре FooOnSteroids, который вызывает ...
- оригинальный метод 'бара' Фу
И результат «Foo, Trait1, Trait2».
Заключение
Если вам удалось все прочитать, ответ на оригинальный вопрос находится в первых четырех строках ...