Почему 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();}
    });
  }
}

2 ответа

Решение

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

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 (которые не имеют дескрипторов методов) в течение некоторого времени...

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

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