Scala provides a @varargs
annotation that generates a Java varargs forwarder method, which makes it possible to write something like this:
import scala.annotation.varargs
class Foo {
@varargs def foo(args: String*): Unit = {
args.foreach(println)
}
}
And then to call this method from Java without needing to create a scala.Seq
:
Foo foo = new Foo();
foo.foo("a", "b");
Which is pretty nice.
Unfortunately the forwarding part doesn't seem to happen when the method is abstract:
trait Bar {
@varargs def bar(args: String*): Unit
}
class Baz extends Bar {
def bar(args: String*): Unit = {
args.foreach(println)
}
}
Now if we have this Java code:
Bar bar = new Baz();
bar.bar("a", "b");
We get this exception (at runtime):
java.lang.AbstractMethodError: Baz.bar([Ljava/lang/String;)V
We can confirm the problem with javap
:
public interface Bar {
public abstract void bar(java.lang.String...);
public abstract void bar(scala.collection.Seq<java.lang.String>);
}
public class Baz implements Bar {
public void bar(scala.collection.Seq<java.lang.String>);
public Baz();
}
So nope, the forwarder definitely isn't getting implemented.
Putting the annotation on both bar
methods fails to compile:
A method with a varargs annotation produces a forwarder method with the same
signature (args: Array[String])Unit as an existing method.
And of course putting the annotation only on the bar
in Baz
means we can't use the forwarder from a Bar
instance.
This seems like it must be a bug, but it also seems extremely easy to run into, and I'm not seeing anything in the issue tracker. Am I using @varargs
correctly? If so, is there a workaround that would make it do what you'd expect here?
I don't know
Scala
, but in Java varargs is just an array.So in Java it will work this way, with one warning:
May be if you can fool
Scala
the same way, you will overcome the bug.