Clojureプロトコルに関するドキュメントには、対応する Java インターフェイスが各プロトコルに対して生成されると記載されています。ただし、プロトコルでできること (任意の型に拡張するなど) は、Java インターフェースに関して単純な実装を持つものとは思えません。プロトコルとプロトコル メソッドは内部でどのように機能しますか? プロトコルごとの Java インターフェイスが必要な理由と方法は?
1 に答える
6
Clojure プラットフォームのソース コードを簡単に調べると、次のことがわかります。
- 各プロトコル メソッドには、Java
Class
からそのプロトコルを実装する Clojure 関数にマップするハッシュテーブルがありますClass
。(実際には、 と呼ばれるカスタム クラスのインスタンスですMethodImplCache
。) - プロトコル メソッドのディスパッチ関数が呼び出されると
Class
、最初の引数の が取得され、ハッシュ テーブルでルックアップが実行されます。 - 何も見つからない場合は、ルックアップが成功するスーパークラスが見つかるまで、最初の引数の継承チェーンをたどります。次に、ディスパッチ
Class
をハッシュテーブルに挿入するため、次回は直接見つけることができます。 - さらに、ルックアップが成功するたびに、最後に見つかっ
Class
たメソッドとそれに対応するメソッドの実装がインスタンス変数にキャッシュされ、Class
次回ディスパッチが同じ場合にハッシュ ルックアップをバイパスするために使用されます。 - プロトコルが任意の Java クラスに拡張された場合、生成されたインターフェースはまったく機能しません。プロトコル メソッドへの呼び出しは
MethodImplCache
、上記のように使用して解決されます。 - 生成されたインターフェースは
reify
、プロトコルを拡張する匿名クラスのインスタンスを取得するために使用する場合、またはプロトコルを拡張する新しいクラスを使用deftype
または作成する場合に使用されます。defrecord
いずれの場合でもClass
、生成されたインターフェースを実装する のオブジェクトが返されます。 - Clojure コンパイラは、プロトコル メソッドの呼び出し用に JVM バイトコードを発行するときに、最初の引数が生成されたインターフェイスのインスタンスであるかどうかをチェックするバイトコードを挿入します。そうであれば、インターフェースの適切なメソッドへの通常の Java メソッド呼び出しを使用します。その場合、上で説明したプロトコルディスパッチ関数は完全にバイパスされ、呼び出されることはありません。
これはすべて、任意のクラスに拡張された場合、プロトコル メソッドの呼び出しが通常の Clojure 関数の呼び出しよりも遅くなることを意味しているようです。reify
//deftype
と一緒に使用した場合のパフォーマンスは、 ordefrecord
を個別に呼び出すよりも、プロトコルをインラインで拡張した方がはるかに優れているはずです。extend-protocol
extend-type
于 2013-09-02T16:19:29.853 に答える