抽象方法和 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.

当然,仅将注释放在 in 上意味着我们不能从实例中使用转发器。barBazBar

这似乎一定是一个错误,但它似乎也非常容易遇到,我在问题跟踪器中没有看到任何东西。我是否正确使用?如果是这样,是否有解决方法可以使其达到您在此处的期望?@varargs


答案 1

我不知道,但在Java中,varargs只是一个数组。Scala

因此,在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...);
}