Почему Scala использует рефлексию для вызова метода структурного типа? - PullRequest
16 голосов
/ 16 декабря 2011

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

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 ]

14 голосов
/ 17 декабря 2011

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

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

10 голосов
/ 17 декабря 2011

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

...