Как объявить метод scala, чтобы его можно было вызвать из Java, используя стиль varargs


У меня есть 2 простых метода в классе библиотеки scala:

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

Все это прекрасно компилируется. Затем я помещаю это в библиотеку foo.jar и пытаюсь скомпилировать следующий фрагмент Java:

import Foo
public class Test {

    public static void main(String[] args) {
        Foo foo = new Foo();
        foo.bar("Hello", "World"); //DOES NOT COMPILE
    }
}

Я могу заменить оскорбительную строку на:

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

Но это, кажется, опровергает точку зрения. Как я могу вызвать его из Java, используя Java varargs-подобный синтаксис?

4 9

4 ответа:

В 2.8 (не знаю о 2.7), если вы переопределяете метод varargs из родительского Java или реализуете метод varargs из интерфейса Java, то компилятор Scala создаст два метода, один для Scala, другой для Java. Java-как вы можете убедиться сами, изучив байт-код-просто берет массив varargs и обертывает его, а затем передает WrappedArray в версию Scala, которая ожидает Seq.

Если есть способ заставить компилятор сгенерировать форвардер под других обстоятельств я не знаю. Я сомневаюсь, что он существует. Похоже, что предоставление способа попросить его (аннотацию, я думаю) было бы разумным запросом на улучшение.

Не совсем уверен, но я думаю, что varargs в Scala использует последовательности и отличается от реализации java. Я думаю, что самый простой способ выполнить то, что вы хотите, - это подкласс класса Foo scala в Java и добавить метод bar, который принимает Java vararg, т. е.

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

Метод JavaFoo количеством аргументов можно назвать, используя синтаксис с переменным числом аргументов в Java.

Надеюсь, это поможет :)

Смотрите ответ на этот вопрос :

Вы можете использовать @annotation.varargs для указания scala генерировать оба метода:

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

Несмотря на полезную подсказку об использовании интерфейса Java , чтобы заставить компилятор Scala быть "совместимым", я просто не мог заставить мой код работать. В конце концов до меня дошло, что методы были объявлены на Scala object , что означает, что они фактически статичны, и вы не можете указать статические методы в интерфейсе, поэтому компилятор в основном просто игнорировал тот факт, что объект реализовал интерфейс. К счастью для меня, есть обходной путь. Вместо того, чтобы позвонить в статический метод непосредственно из Java, как это:

CLASS.method(x,y,z)

Вы должны назвать это так:

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

Это означает, что вы фактически обращаетесь к объектуsingleton как к экземпляру, на который ссылается статическое поле, и поскольку это экземпляр и реализует интерфейс java, там компилятор выполняет свою работу должным образом и реализует метод varargs, так что Java может вызвать его как varargs.

Лично я считаю, что это следует рассматривать как ошибку компилятора Scala.