Абстрактные методы и аннотация varargs
Scala предоставляет @varargs
аннотацию, которая генерирует метод пересылки Java varargs, что позволяет написать что-то вроде этого:
import scala.annotation.varargs
class Foo {
@varargs def foo(args: String*): Unit = {
args.foreach(println)
}
}
И затем, чтобы вызвать этот метод из Java, не создавая scala.Seq
:
Foo foo = new Foo();
foo.foo("a", "b");
Что довольно приятно.
К сожалению, часть пересылки, похоже, не возникает, когда метод является абстрактным:
trait Bar {
@varargs def bar(args: String*): Unit
}
class Baz extends Bar {
def bar(args: String*): Unit = {
args.foreach(println)
}
}
Теперь, если у нас есть этот код Java:
Bar bar = new Baz();
bar.bar("a", "b");
Мы получаем это исключение (во время выполнения):
java.lang.AbstractMethodError: Baz.bar([Ljava/lang/String;)V
Мы можем подтвердить проблему с помощью 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();
}
Таким образом, форвардер определенно не реализуется.
Помещение аннотаций к методам bar
не удается скомпилировать:
A method with a varargs annotation produces a forwarder method with the same
signature (args: Array[String])Unit as an existing method.
И, конечно, размещение аннотации только на bar
в Baz
означает, что мы не можем использовать пересылку из экземпляра bar
.
Кажется, что это ошибка, но это также кажется чрезвычайно простым в использовании, и я не вижу ничего в трекер проблема. Правильно ли я использую @varargs
? Если это так, есть ли обходной путь, который заставит его делать то, что вы ожидаете здесь?
Ответы
Ответ 1
Я не знаю Scala
, но в Java varargs - это просто массив.
Итак, в Java он будет работать таким образом, с одним предупреждением:
package tests.StackOverflow.q27052394;
import java.util.Arrays;
public class Runner {
public interface Bar {
public abstract void bar(java.lang.String ... ss);
}
public static class Baz implements Bar {
public void bar(java.lang.String[] array) {
System.out.println(Arrays.toString(array));
}
}
public static void main(String[] args) {
Bar b = new Baz();
b.bar("hello", "world");
}
}
Может быть, если вы можете обмануть Scala
таким же образом, вы преодолеете ошибку.
Ответ 2
Одним из способов достижения желаемого является определение интерфейса на Java следующим образом
interface Bar {
public void bar(String... args);
}
а затем для определения реализации обычно в Scala следующим образом
class Baz extends Bar {
def bar(args: String*): Unit = {
args.foreach(println)
}
}
Scala компилятор распознает, что ему нужно создать метод стиля vargs и будет генерировать как varargs, так и реализацию Seq.
Выход javap, как и ожидалось
public class Baz implements Bar {
public void bar(scala.collection.Seq<java.lang.String>);
public void bar(java.lang.String[]);
public Baz();
}
interface Bar {
public abstract void bar(java.lang.String...);
}