17

関数が構造型を受け入れる場合、次のように定義できます。

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

私の質問は、なぜ反射なのかということです。リフレクションの代わりにanonymousを使用して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();}
    });
  }
}
4

2 に答える 2

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

Different ducksそれはあなたの提案の下に印刷されます。さらに改良することもできますが、プロキシを使用して参照の同等性を維持することはできません。

考えられる解決策は、型クラスパターンを使用することですが、これには別のパラメーターを渡す必要があります(暗黙的であっても)。それでも、それはより速いです。しかし、それは主にJavaの反射速度の跛行によるものです。うまくいけば、メソッドハンドルが速度の問題を回避するでしょう。残念ながら、Scalaはしばらくの間Java 5、6、7(メソッドハンドルを持たない)をあきらめる予定はありません...

于 2011-12-16T20:11:42.703 に答える
10

構造型のメソッドを実装するプロキシオブジェクトに加えて、Any(equals、hashCode、toString、isInstanceOf、asInstanceOf)およびAnyRef(getClass、wait、notify)のすべてのメソッドの適切なパススルー実装も必要になります。 、notifyAll、および同期)。これらのいくつかは簡単ですが、正しく理解することはほとんど不可能です。特に、リストされているすべてのメソッドは(Javaの互換性とセキュリティのために)AnyRefで「最終」であるため、プロキシオブジェクトで適切に実装できませんでした。

于 2011-12-16T20:12:56.183 に答える