Подтвердить что ты не робот

Абстрактные методы и аннотация 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? Если это так, есть ли обходной путь, который заставит его делать то, что вы ожидаете здесь?

4b9b3361

Ответ 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...);
}