前回書いた、InfinispanのQuery DSLを使った時に、これにかなりハマりました。
Scalaには、AnyRefクラスにeqというメソッドが定義してあります。
final def eq(that: AnyRef): Boolean
Scalaでクラスを書く場合は、わざわざこのメソッドと衝突するようなものを書こうと思わない(finalだし…)でしょうが、Java側で、しかもインターフェースで定義してあった場合にはちょっと苦労します。
簡単な例を出しましょう。
たとえば、こういうインターフェースを定義します。
src/main/java/HasEqInterface.java
public interface HasEqInterface { String eq(Object target); }
引数がObjectのeqメソッドです。戻り値の型は、まあ何でもいいです。
で、このインターフェースを実装したクラスを用意します。
src/main/java/HasEqImpl.java
public class HasEqImpl implements HasEqInterface { private String value; public HasEqImpl(String value) { this.value = value; } @Override public String eq(Object target) { return Boolean.valueOf(value.equals(target.toString())) .toString(); } }
これをScalaから呼んでみます。
まずはインターフェースの型で扱うパターン。
val interfaceVal: HasEqInterface = new HasEqImpl("interfaceVal")
eqメソッドを呼び出してみましょう。
val result: String = interfaceVal.eq("interfaceVal")
コンパイルしてみると、
type mismatch; [error] found : Boolean [error] required: String [error] val result: String = interfaceVal.eq("interfaceVal") [error] ^ [error] one error found [error] (compile:compile) Compilation failed
コンパイルエラーになります…。どう見ても、AnyRef#eqを参照しています。
ところがですね、ここでインターフェースの型ではなく、クラスに落とすと
val result: String = interfaceVal.asInstanceOf[HasEqImpl].eq("interfaceVal")
コンパイル可能になります。
> compile [info] Compiling 1 Scala source to /xxxxx/target/scala-2.10/classes... [success] Total time: 1 s, completed 2013/12/15 21:01:13
この理屈なので、最初からクラスの型で扱っていればコンパイル可能です。
val classVal: HasEqImpl = new HasEqImpl("classVal") val result: String = classVal.eq("classVal")
Scala から S2JDBC を利用するときに気をつけること
http://d.hatena.ne.jp/NetPenguin/20100406
Mockitを使う場合があるようです。
SpecsでMockitoのAnswerとArgumentCaptorを使う
http://d.hatena.ne.jp/takezoe/20110614
Using Mockito in a Scala unit test
http://lizdouglass.wordpress.com/2010/12/15/using-mockito-in-a-scala-unit-test/
で、今回はもうちょっとハマる例でしたが、最終的にStack Overflowにこの内容が書かれていました。
How to call T eq(Object) method of Java interface from Scala?
http://stackoverflow.com/questions/7263861/how-to-call-t-eqobject-method-of-java-interface-from-scala
というわけで、ここでの結論は
Javaのインターフェースで定義されたeq(Object)メソッドは、Scalaから呼び出す時は実装したクラスにキャストしたり、実装したクラスの型で扱いましょう
ということですね。
自分がハマったケースは、InfinispanのQuery DSLにeq(Object)なメソッドがあって、しかもインターフェース定義、とどめに実装クラスがパッケージプライベートのため見えなくて、完全に詰みました…。
このインターフェースで定義されたeq(Object)メソッド
FilterConditionEndContext
https://github.com/infinispan/infinispan/blob/master/query-dsl/src/main/java/org/infinispan/query/dsl/FilterConditionEndContext.java
を実装した、このクラスが見えないからです…。
AttributeCondition
https://github.com/infinispan/infinispan/blob/master/query-dsl/src/main/java/org/infinispan/query/dsl/impl/AttributeCondition.java
というわけで、先のInfinispan Query DSLの使用時にはeqメソッドを使うことは諦めましたが(同じパッケージにすれば、もちろん使えましたが…)、投げるQueryの内容が他のメソッドでも代用できることがわかったので、それで逃げました。