CLOVER🍀

That was when it all began.

クラスをパラメータ化して、その型パラメータをメソッドの戻り値の型にしてJavaから呼び出すとAbstractMethodErrorになるという話

あんた何言ってんの?みたいなタイトルですが、ちょっとハマったので。

Scala 2.11.6+sbt 0.13.8で試しています。

こういうJavaのインターフェースを用意して
src/main/java/org/littlewing/HasVarArgsWithGenerics.java

package org.littlewing;

public interface HasVarArgsWithGenerics<T> {
    T method(Object... arguments);
}

このインターフェースの実装を、Scalaで用意します。
src/main/scala/org/littlewings/HasVarArgsWithGenericsImpl.scala

package org.littlewings

import org.littlewing.HasVarArgsWithGenerics

class HasVarArgsWithGenericsImpl extends HasVarArgsWithGenerics[String] {
  override def method(arguments: AnyRef*): String = getClass.getSimpleName
}

この状態で、Javaからこのクラスを呼び出すと

        HasVarArgsWithGenerics hasVarArgs = new HasVarArgsWithGenericsImpl();
        hasVarArgs.method("arg1", "arg2", "arg3");

AbstractMethodErrorになります。

java.lang.AbstractMethodError: org.littlewings.HasVarArgsWithGenericsImpl.method([Ljava/lang/Object;)Ljava/lang/Object;

どうも条件は、可変長引数を持っていること、Javaのクラスレベルで型パラメータを定義しておき、それをメソッドの戻りの型に使っていると起きそうな感じなのですが…。
メソッド宣言に型パラメータを入れただけだと、OKでした。

Scalaだと、問題なく呼べます。

  val hasVarArgsWithGenerics = new HasVarArgsWithGenericsImpl
  hasVarArgsWithGenerics.method("arg1", "arg2", "arg3")

また、単に可変長引数を持っているだけであれば、別に問題ありません。
src/main/java/org/littlewing/HasVarArgs.java

package org.littlewing;

public interface HasVarArgs {
    void method(Object... arguments);
}

実装側。
src/main/scala/org/littlewings/HasVarArgsImpl.scala

package org.littlewings

import org.littlewing.HasVarArgs

class HasVarArgsImpl extends HasVarArgs {
  override def method(arguments: AnyRef*): Unit = ()
}

これであれば、JavaからもScalaからも普通に呼べます。

また、先ほどのAbstractMethodErrorになる例で、以下のようにScala側の実装クラスを直接指すと

            HasVarArgsWithGenericsImpl hasVarArgs = new HasVarArgsWithGenericsImpl();
            hasVarArgs.method("arg1", "arg2", "arg3");

コンパイルエラーになります。

[error]   期待値: scala.collection.Seq<java.lang.Object>
[error]   検出値: java.lang.String,java.lang.String,java.lang.String
[error]   理由: 実引数リストと仮引数リストの長さが異なります
[error] hasVarArgs.method

まあ、ね…。

自分は、JCacheのEntryProcessorをScalaで実装して、AbstractMethodErrorが飛んできてハマったのでJavaで書き直しました。

ふーん…。