开发者

How to declare scala method so that it can be called from Java using varargs style

I have 2 simple methods in a scala library class:

class Foo {
  def bar(args : String*) : Unit = println("Foo.bar with: " + args)
  def bar(args : Array[String]) : Unit = bar(args.toSeq : _*)
}

This all compiles nicely. I then put this in a library foo.jar and try and compile the following piece of Java:

import Foo
public class Test {

    public static void main(String[] args) {
        Foo foo = new Foo();
        foo.bar("Hello", "World"); //DOES NOT COMPILE
    }
}
开发者_如何学运维

I can replace the offending line with:

foo.bar(new String[] { "Hello", "World" }); //OK

But this seems to defeat the point. How can I call it from Java using Java varargs-like syntax?


In 2.8 (dunno about 2.7), if you override a varargs method from a Java parent, or implement a varargs method from a Java interface, then the Scala compiler will generate two methods, one for Scala, one for Java. The Java one -- as you can see for yourself by inspecting the bytecode — simply takes the varargs array and wraps it, then passed the WrappedArray to the Scala version that's expecting a Seq.

If there is a way to force the compiler to generate the forwarder under other circumstances, I don't know about it. I doubt it exists. Seems like providing a way to ask for it (an annotation, I guess) would be a reasonable enhancement request.


Not entierly certain but I think varargs in Scala uses Sequences and are different from the java implementation. I think the easiest way to accomplish what you want is to subclass the Foo scala class in Java and add a bar method that takes a Java vararg i.e.

public class JavaFoo extends Foo {
    public void bar(String... args) {
         super.bar(args)
    }
}

The JavaFoo vararg method can then be called using vararg syntax in Java.

Hope it helps :)


See the answer to this question:

You can use the @annotation.varargs to instruct scala to generate both methods:

class Foo {
  @annotation.varargs def bar(args : String*) : Unit = println("Foo.bar with: " + args)
  def bar(args : Array[String]) : Unit = bar(args.toSeq : _*)
}


Despite the useful hint about using a Java interface to force the Scala compiler to be "compatible", I just couldn't get my code to work. Eventually it dawned on me that the methods were declared on a Scala object, which means they are in fact static, and you cannot specify static methods in an interface, so the compiler was basically just ignoring the fact that the object implemented the interface. Luckily for me, there is a work-around. Instead of calling the static method directly from Java like this:

CLASS.method(x,y,z)

You have to call it like this:

CLASS$.MODULE$.method(x,y,z)

This means you are in fact accessing the singleton object as an instance referred by a static field, and since it is an instance, and implements the java interface, there the compiler does it's job properly and implements the varargs method so that Java can call it as a varargs.

I personally think this should be regarded as a Scala compiler bug.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜