抽象方法和变长参数注解

13

Scala 提供了 @varargs 注解,它会生成一个 Java 可变参数转发方法,使得我们可以像这样编写代码:

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.

当然,仅在Baz中的bar上放置注解意味着我们无法使用来自Bar实例的转发器。

这似乎像是个bug,但很容易遇到,并且我在问题跟踪器中没有看到任何信息。我的@varargs使用正确吗?如果是,是否有一种解决方法可以让它执行你在这里期望的操作?


哎呀,似乎又出现了一个 bug。多亏了另一个“jugaad”解决方案的互操作性。天啊!还有多少个潜藏着? - Jatin
看起来像是一个 bug。尝试在他们的问题跟踪器中提交一个新的 bug。 - Luminous
2个回答

2

我不懂Scala,但在Java中可变参数只是一个数组。

因此,在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,您就可以克服这个错误。


将方法 def bar(args: Array[String]): Unit = bar(args: _*) 添加到 Baz 中似乎确实可行(尽管不是很愉快)。 - Travis Brown

1

实现您想要的一种方法是在Java中定义接口,如下所示:

interface Bar {
  public void bar(String... args);
}

然后在Scala中通常定义实现如下
class Baz extends Bar {
  def bar(args: String*): Unit = {
    args.foreach(println)
  }
}

Scala编译器将会识别需要生成可变参数风格方法,并且会同时生成可变参数和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...);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接