1

Clojureプロトコルに関するドキュメントには、対応する Java インターフェイスが各プロトコルに対して生成されると記載されています。ただし、プロトコルでできること (任意の型に拡張するなど) は、Java インターフェースに関して単純な実装を持つものとは思えません。プロトコルとプロトコル メソッドは内部でどのように機能しますか? プロトコルごとの Java インターフェイスが必要な理由と方法は?

4

1 に答える 1

6

Clojure プラットフォームのソース コードを簡単に調べると、次のことがわかります。

  1. 各プロトコル メソッドには、JavaClassからそのプロトコルを実装する Clojure 関数にマップするハッシュテーブルがありますClass。(実際には、 と呼ばれるカスタム クラスのインスタンスですMethodImplCache。)
  2. プロトコル メソッドのディスパッチ関数が呼び出されるとClass、最初の引数の が取得され、ハッシュ テーブルでルックアップが実行されます。
  3. 何も見つからない場合は、ルックアップが成功するスーパークラスが見つかるまで、最初の引数の継承チェーンをたどります。次に、ディスパッチClassをハッシュテーブルに挿入するため、次回は直接見つけることができます。
  4. さらに、ルックアップが成功するたびに、最後に見つかっClassたメソッドとそれに対応するメソッドの実装がインスタンス変数にキャッシュされ、Class次回ディスパッチが同じ場合にハッシュ ルックアップをバイパスするために使用されます。
  5. プロトコルが任意の Java クラスに拡張された場合、生成されたインターフェースはまったく機能しません。プロトコル メソッドへの呼び出しはMethodImplCache、上記のように使用して解決されます。
  6. 生成されたインターフェースreify、プロトコルを拡張する匿名クラスのインスタンスを取得するために使用する場合、またはプロトコルを拡張する新しいクラスを使用deftypeまたは作成する場合に使用されます。defrecordいずれの場合でもClass、生成されたインターフェースを実装する のオブジェクトが返されます。
  7. Clojure コンパイラは、プロトコル メソッドの呼び出し用に JVM バイトコードを発行するときに、最初の引数が生成されたインターフェイスのインスタンスであるかどうかをチェックするバイトコードを挿入します。そうであれば、インターフェースの適切なメソッドへの通常の Java メソッド呼び出しを使用します。その場合、上で説明したプロトコルディスパッチ関数は完全にバイパスされ、呼び出されることはありません。

これはすべて、任意のクラスに拡張された場合、プロトコル メソッドの呼び出しが通常の Clojure 関数の呼び出しよりも遅くなることを意味しているようです。reify//deftypeと一緒に使用した場合のパフォーマンスは、 ordefrecordを個別に呼び出すよりも、プロトコルをインラインで拡張した方がはるかに優れているはずです。extend-protocolextend-type

于 2013-09-02T16:19:29.853 に答える