Использование признаков 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".
вывод
Если вам удалось прочитать все, ответ на вопрос оригинальный вопрос находится в первых четырех строках...