Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 782
oxbow_lakes Avatar asked Jun 22 '10 14:06

oxbow_lakes


3 Answers

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.

like image 93
Seth Tisue Avatar answered Oct 24 '22 10:10

Seth Tisue


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 :)

like image 21
Emil H Avatar answered Oct 24 '22 08:10

Emil H


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 : _*)
}
like image 1
iain Avatar answered Oct 24 '22 09:10

iain