Kenji Yoshida
Kenji Yoshida

Reputation: 3118

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

Upvotes: 8

Views: 4570

Answers (2)

fifth
fifth

Reputation: 63

I found same problem. When i set Scala code return type explicitly,it worked.Just like:

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

Upvotes: 0

Rex Kerr
Rex Kerr

Reputation: 167871

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).

Upvotes: 8

Related Questions