为什么scala使用反射来调用结构类型的方法?

Op *_*kel 16 scala structural-typing

如果函数接受结构类型,则可以定义为:

def doTheThings(duck: { def walk; def quack }) { duck.quack }
Run Code Online (Sandbox Code Playgroud)

要么

type DuckType = { def walk; def quack  }
def doTheThings(duck: DuckType) { duck.quack }
Run Code Online (Sandbox Code Playgroud)

然后,您可以通过以下方式使用该功能:

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

def main(args: Array[String]) {
    doTheThings(new Dog);
}
Run Code Online (Sandbox Code Playgroud)

如果您为我的示例反编译(到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();}
    });
  }
}
Run Code Online (Sandbox Code Playgroud)

Dan*_*ral 15

考虑一个简单的命题:

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
Run Code Online (Sandbox Code Playgroud)

它会Different ducks根据您的提案打印出来.您可以进一步优化它,但是您无法使用代理保持参照等式完整.

一种可能的解决方案是使用类型类模式,但这需要传递另一个参数(即使是隐式的).不过,它更快.但这主要是因为Java反射速度的跛足.希望方法句柄可以解决速度问题.不幸的是,Scala没有计划放弃Java 5,6和7(没有方法句柄)一段时间......

  • 该调查导致决定暂时保持1.6左右,并增加1.7代码生成的实验支持,可能的目标是在2.11之后的Scala的下一版本中直接跳过1.7到1.8.http://grokbase.com/t/gg/scala-internals/1337pmhy48/scala-2-11-may-require-java-7 (2认同)

Dav*_*ith 10

除了在结构类型上实现方法的代理对象之外,它还需要在Any(equals,hashCode,toString,isInstanceOf,asInstanceOf)和AnyRef(getClass,wait,notify)上具有所有方法的适当传递实现. ,notifyAll和synchronized).虽然其中一些是直截了当的,但有些人几乎不可能做对.特别是,所有列出的方法都是AnyRef上的"final"(用于Java兼容性和安全性),因此代理对象无法正确实现.