【问题标题】:Why scala uses reflection to call method on structural type?为什么scala使用反射来调用结构类型的方法?
【发布时间】:2011-12-16 19:55:03
【问题描述】:

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

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);
}

如果你对我的例子中由 scalac 生成的类进行反编译(Java),你可以看到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();}
    });
  }
}

【问题讨论】:

    标签: scala structural-typing


    【解决方案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(它们没有方法句柄)......

    【讨论】:

    • 我没听懂最后一句话,你能解释一下吗?
    • @om-nom-nom invokevirtual 在 JVM 1.5 和 1.6 上不存在,因此 Scala 不能依赖它。 Scala 2.10 实际上会弃用 JVM 1.5,但距离 Scala 能够利用仅存在于 JVM 1.7 上的东西还有一段时间。
    • @Daniel C. Sobral:我猜你在上一条评论中的意思是 invokedynamic 而不是 invokevirtual
    • @OpDeCirkel 你是对的。使用invokedynamic 将替代正在做的事情,不会遭受问题提出的解决方案的身份丢失,但取决于不支持Java 1.5/1.6。这不再是牵强附会了——有一个社区民意调查是否要求 Scala 2.11 需要 Java 7。我不知道结果如何,但这是现在正在考虑的事情。
    • 投票结果决定暂时保留 1.6,并为 1.7 代码生成添加实验性支持,无论下一个 Scala 版本如何,都可能从 1.7 直接跳到 1.8将在 2.11 之后。 grokbase.com/t/gg/scala-internals/1337pmhy48/…
    【解决方案2】:

    除了您的代理对象在结构类型上实现方法之外,它还需要对 Any(equals、hashCode、toString、isInstanceOf、asInstanceOf)和 AnyRef(getClass,等待、通知、通知所有和同步)。虽然其中一些是直截了当的,但有些几乎不可能做到正确。特别是,列出的所有方法在 AnyRef 上都是“最终的”(为了 Java 兼容性和安全性),因此您的代理对象无法正确实现。

    【讨论】:

    • @Daniel C. Sobral 和 Dave Griffith:你的两个答案都是可以接受的。所以我不得不抛硬币正式接受。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多