型またはクラスでディスパッチする場合は、Protocolsを使用するようにコードを修正することを検討してください。
より詳細な回答:
Clojuredefmulti
では、正確なサブタイプも登録されている場合、親の Java タイプにディスパッチできません。これは意図的なものです (リスコフの置換原理の議論で「最小の驚き」の側に立っています)。Y の登録済みマルチメソッドが既に存在するため、オブジェクトisa?
が正確に Y の場合、Y のメソッドをヒットします。これは「X」でもあります。Java では、クラスは複数のインターフェイスから継承できますが、厳密に 1 つの型にしかなれません。それがあなたがここでぶつかっているものです。
マルチメソッドのドキュメントごと
派生は、Java 継承 (クラス値の場合) の組み合わせ、または Clojure のアドホック階層システムの使用によって決定されます。階層システムは、名前 (シンボルまたはキーワード) 間の派生関係、およびクラスと名前の間の関係をサポートします。派生関数はこれらの関係を作成し、isa?
関数はそれらの存在をテストします。ではないことに注意してください。isa?
instance?
のソースを調べると、MultiFn
Clojure が特定のマルチメソッド ディスパッチ値 (型でディスパッチする場合) で最も具体的な Java クラスを常に使用することがわかります。
の実装については、Clojure 1.4.0 ソースのこの行をMultiFn
参照してくださいdominates
。
REPL で:
user=> (defmulti foo #(class %))
user=> (defmethod foo java.util.RandomAccess [x] "RandomAccess")
user=> (defmethod foo java.util.Vector [x] "Vector")
user=> (defmethod foo java.util.Stack [x] "Stack")
OK、これは期待どおりに機能し、Java 型を最初に検査するprefer-method
ため、クラス階層をオーバーライドできません。isa?
user=> (prefer-method foo java.util.RandomAccess java.util.Stack)
user=> (foo (new java.util.Stack))
"Stack"
最後に、ソース内でMultiFn
、ディスパッチ値の型に一致するすべてのメソッド キーを検査します ( に従ってisa?
)。複数の一致が見つかった場合は、型の階層を調べて「優勢」な値を探します。ここでStack
支配していることがわかりますRandomAccess
user=> (isa? java.util.Stack java.util.RandomAccess)
true
user=> (isa? java.util.RandomAccess java.util.Stack)
false
ここで、次のように新しいメソッドを定義するbar
とします。
user=> (defmulti bar #(class %))
user=> (defmethod bar Comparable [x] "Comparable")
user=> (defmethod bar java.io.Serializable [x] "Serializable")
あいまいさのために、次のようになります。
user=> (bar 1)
IllegalArgumentException Multiple methods in multimethod 'bar' match dispatch value: class java.lang.Long -> interface java.lang.Comparable and interface java.io.Serializable, and neither is preferred clojure.lang.MultiFn.findAndCacheBestMethod (MultiFn.java:136)
今、私はこの問題を解決することができますprefer-method
user=> (prefer-method bar Comparable java.io.Serializable)
user=> (bar 1)
"Comparable"
ただし、新しいメソッドを登録するとLong
user=> (defmethod bar Long [x] "Long")
user=> (bar 1)
"Long"
Comparable
を使用してもに戻れませんprefer-method
:
user=> (prefer-method bar Comparable Long)
user=> (bar 1)
"Long"
それがあなたがここで遭遇したもののようです。
-のオプションがあることに注意してくださいremove-method
-しかし、それはあなたが考案した「ハック」と比較して、はるかに重い/危険な(モンキーパッチ?) ソリューションだと思います。