Использование черт 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,

Другие вопросы по тегам