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

时间:2011-12-16 19:55:04

标签: scala structural-typing

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

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 个答案:

答案 0 :(得分:14)

考虑一个简单的命题:

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(它没有方法句柄)一段时间......

答案 1 :(得分:10)

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