Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AbstractMethodError occurred when package private method override

I noticed that AbstractMethodError occurred when execute the following codes.

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)

Is this a bug or specification ?

P.S. Scala version 2.9.2 , 2.10.0-M3 and 2.10.0-SNAPSHOT (2.10.0-20120507-163905-843ac9520c) same error occurred

like image 240
Kenji Yoshida Avatar asked May 11 '12 17:05

Kenji Yoshida


1 Answers

It's a bit complicated. It's a bug--the Scala compiler should emit an error--but it's a somewhat subtle one.

The problem is that since you have left public off of the abstract method, its visibility is package-private. If you write this in 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));
  }
}

then the Java compiler will complain:

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

It clearly is trying to override, but it doesn't actually work since it's not in the right package and therefore can't actually access (or override) the original foo. If we change the package to javapkg, then everything works. If we try this Scala instead:

package javapkg

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

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

then Scala works fine also.

It is the fault of Scala's compiler that it doesn't notice this mistake--it should emit the same error as the Java compiler (or a more helpful one that actually tells you that the access is screwy!) rather than emitting bytecode that cannot work.

But it's easy to fix: just change the package on the Scala code (or the access modifier in the Java code).

like image 118
Rex Kerr Avatar answered Oct 22 '22 06:10

Rex Kerr