当覆盖包私有方法时发生了AbstractMethodError错误。

8
我注意到在执行以下代码时发生了AbstractMethodError错误。
package javapkg;

public abstract class JavaClass{
  abstract String foo(int b);

  public String bar(int b){
    return foo(b);
  }
}

package scalapkg

import javapkg._

class ScalaClass extends JavaClass{
  def foo(a:Int) = a.toString
}

object Main extends App{
  println(new ScalaClass().bar(10))
}

[error] (run-main) java.lang.AbstractMethodError: javapkg.JavaClass.foo(I)Ljava/lang/String;
java.lang.AbstractMethodError: javapkg.JavaClass.foo(I)Ljava/lang/String;
at javapkg.JavaClass.bar(JavaClass.java:7)
at scalapkg.Main$delayedInit$body.apply(ScalaClass.scala:10)
at scala.Function0$class.apply$mcV$sp(Function0.scala:34)
at scala.runtime.AbstractFunction0.apply$mcV$sp(AbstractFunction0.scala:12)
at scala.App$$anonfun$main$1.apply(App.scala:60)
at scala.App$$anonfun$main$1.apply(App.scala:60)
at scala.collection.LinearSeqOptimized$class.foreach(LinearSeqOptimized.scala:59)
at scala.collection.immutable.List.foreach(List.scala:76)
at scala.collection.generic.TraversableForwarder$class.foreach(TraversableForwarder.scala:30)
at scala.App$class.main(App.scala:60)
at scalapkg.Main$.main(ScalaClass.scala:9)
at scalapkg.Main.main(ScalaClass.scala)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
java.lang.RuntimeException: Nonzero exit code: 1
at scala.sys.package$.error(package.scala:27)

这是一个 bug 还是规范问题?

附注:在 Scala 版本 2.9.2、2.10.0-M3 和 2.10.0-SNAPSHOT(2.10.0-20120507-163905-843ac9520c)中都发生了同样的错误。


我想知道,如果这是一个 bug 的话,这个问题是否已经在 https://issues.scala-lang.org/ 上注册了。 - Kenji Yoshida
2个回答

8

这有点复杂。这是一个bug - Scala编译器应该发出错误提示,但它是一个比较微妙的bug。

问题在于,由于您没有在抽象方法上加上public,因此其可见性为包级私有。如果您在Java中编写以下内容:

package javapkgimpl;

public class JavaClassImpl extends javapkg.JavaClass {
  String foo(int b) { return (new java.lang.Integer(b)).toString(); }
  public static void main(String[] args) {
    System.out.println(new JavaClassImpl().bar(10));
  }
}

如果是这样,Java编译器会报错:
JavaClassImpl.java:3: javapkgimpl.JavaClassImpl is not abstract and does not
  override abstract method foo(int) in javapkg.JavaClass
public class JavaClassImpl extends javapkg.JavaClass {
       ^
1 error

显然,它试图覆盖,但实际上由于不在正确的包中,因此无法访问(或覆盖)原始的foo。 如果我们将包更改为javapkg,则一切都能正常工作。如果我们尝试使用Scala:

package javapkg

class ScalaClass extends JavaClass{
  def foo(a:Int) = a.toString
}

object ScalaClass extends App{
  println(new ScalaClass().bar(10))
}

那么Scala也可以正常工作。

这是Scala编译器的错误,它没有注意到这个错误 - 它应该发出与Java编译器相同的错误(或更有帮助的错误消息,告诉您访问出现问题!),而不是生成无法工作的字节码。

但很容易修复:只需更改Scala代码上的包(或Java代码中的访问修饰符)。


0

我遇到了同样的问题。当我明确设置Scala代码的返回类型时,它就可以工作。就像这样:

class ScalaClass extends JavaClass{
  def foo(a:Int):String = a.toString
}

1
你确定它在完全相同的情况下有帮助吗? - Alexey Romanov
是的。我的Java接口方法返回void,而Scala方法代码实现Java时如果不使用Unit返回类型,它可以正确编译,但在运行时会报告“AbstractMethodError”。如果显式添加Unit修饰符,则可以成功运行。_英语不好,一切都取决于Google_。 - fifth

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