Почему scala использует метод отражения для вызова по структурному типу?

Если функция принимает структурный тип, ее можно определить как:

def doTheThings(duck: { def walk; def quack }) { duck.quack }

или

type DuckType = { def walk; def quack  }
def doTheThings(duck: DuckType) { duck.quack }

Затем вы можете использовать эту функцию следующим образом:

class Dog {
    def walk { println("Dog walk") }
    def quack { println("Dog quacks") }
}

def main(args: Array[String]) {
    doTheThings(new Dog);
}

Если вы декомпилируете (для Java) классы, сгенерированные scalac для моего примера, вы можете видеть, что аргумент doTheThings имеет тип Object, и реализация использует отражение для вызова методов в аргументе (т.е. duck.quack)

Мой вопрос - почему размышление? Не возможно ли использовать анонимный и invokevirtual вместо отражения?

Вот способ перевести (реализовать) структурный вызов типа для моего примера (синтаксис Java, но точка - это байт-код):

class DuckyDogTest {
  interface DuckType {
    void walk();
    void quack();
  }

  static void doTheThing(DuckType d) {
    d.quack();
  }

  static class Dog {
    public void walk() { System.out.println("Dog walk"); }
    public void quack() { System.out.println("Dog quack"); }
  }

  public static void main(String[] args) {
    final Dog d = new Dog();
    doTheThing(new DuckType() {
      public final void walk() { d.walk(); }
      public final void quack() { d.quack();}
    });
  }
}

Ответы

Ответ 1

Рассмотрим простое предложение:

type T = { def quack(): Unit; def walk(): Unit }
def f(a: T, b: T) = 
  if (a eq b) println("They are the same duck!")
  else        println("Different ducks")

f(x, x) // x is a duck

Он напечатает Different ducks по вашему предложению. Вы могли бы дополнительно его уточнить, но вы просто не можете сохранить ссылочное равенство без использования прокси.

Возможным решением будет использование шаблона типа типа, но для этого потребуется передать другой параметр (даже если неявный). Тем не менее, это быстрее. Но это в основном из-за хромоты скорости отражения Java. Хотелось бы надеяться, что обработчики методов обойдутся проблемой скорости. К сожалению, Scala не планирует отказываться от Java 5, 6 и 7 (которые не имеют дескрипторов методов) в течение некоторого времени...

Ответ 2

В дополнение к вашим методам реализации прокси-объекта в структурном типе также необходимо иметь соответствующие сквозные реализации всех методов для Any (equals, hashCode, toString, isInstanceOf, asInstanceOf) и AnyRef (getClass, ждать, уведомлять, уведомлять все и синхронизировать). Хотя некоторые из них были бы простыми, некоторые из них были бы почти невозможны. В частности, все перечисленные методы являются "окончательными" для AnyRef (для совместимости и безопасности Java) и поэтому не могут быть правильно реализованы вашим прокси-объектом.