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

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".

вывод

Если вам удалось прочитать все, ответ на вопрос оригинальный вопрос находится в первых четырех строках...

это действительно не абстрактно, так как bar возвращается пустой Unit (типа НОП). Попробуйте:

trait Trait {
  def bar: Unit
}

затем bar будет возвращать абстрактный метод Java void.