Использование признаков Scala с реализованными методами в Java
Я думаю, что невозможно вызвать методы, реализованные в чертах Scala из Java, или есть способ?
Предположим, у меня в Scala:
trait Trait {
def bar = {}
}
и в Java, если я использую его как
class Foo implements Trait {
}
Java жалуется, что 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" }
вот новый " Фу " на стероидах:
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 ()' на Фоонстероидах экземпляр, который вызывает ...
- статический метод " bar "класса Trait1$передает это как аргумент и возвращает конкатенацию вызова метода" Trait1$$super$bar () "и строки", Trait1";
- 'Trait1$$super$bar' на экземпляре FooOnSteroids, который вызывает ...
- оригинальный метод Foo 'bar'
и результат "Foo, Trait1, Trait2".
вывод
Если вам удалось прочитать все, ответ на вопрос оригинальный вопрос находится в первых четырех строках...