Использование черт Scala с реализованными методами в Java
Я думаю, что невозможно вызвать методы, реализованные в чертах Scala из Java, или есть способ?
Предположим, у меня есть в Scala:
trait Trait {
def bar = {}
}
и в Java, если я использую его как
class Foo implements Trait {
}
Ява жалуется, что Trait is not abstract and does not override abstract method bar() in Trait
2 ответа
Ответ
С точки зрения 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
это интерфейс- Аннотация
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".
Заключение
Если вам удалось все прочитать, ответ на оригинальный вопрос в первых четырех строках...
Это действительно не абстрактно, так как bar
возвращается пустой Unit
(своего рода НОП). Пытаться:
trait Trait {
def bar: Unit
}
затем bar
будет абстрактный Java-метод, возвращающий void
,