AbstractMethodError when inheriting trait from java abstract class and referencing `val` from the trait
reproduction steps
using Scala (2.13.6),
trait ScalaTrait {
def foo1: String
def foo2: String = "default foo value 2"
val val1: String
val val2: String = "default val value 2"
}
public abstract class JavaAbstractClass implements ScalaTrait {
public String javaFoo1() { return foo1(); }
public String javaFoo2() { return foo2(); }
public String javaVal1() { return val1(); }
public String javaVal2() { return val2(); }
}
import scala.util.Try
object ScalaClass extends JavaAbstractClass {
override def foo1: String = "implemented foo in scala 1"
override val val1: String = "implemented val in scala 1"
def main(args: Array[String]): Unit = {
println(Try(javaFoo1()))
println(Try(javaFoo2()))
println(Try(javaVal1()))
println(Try(javaVal2()))
}
}
problem
The program compiles fine, but an exception is thrown at runtime:
Success(implemented foo in scala 1)
Success(default foo value 2)
Success(implemented val in scala 1)
Exception in thread "main" java.lang.AbstractMethodError: Method ScalaClass$.val2()Ljava/lang/String; is abstract
at ScalaClass$.val2(ScalaClass.scala)
at JavaAbstractClass.javaVal2(JavaAbstractClass.java:6)
at ScalaClass$.$anonfun$main$4(ScalaClass.scala:12)
at scala.util.Try$.apply(Try.scala:210)
at ScalaClass$.main(ScalaClass.scala:12)
at ScalaClass.main(ScalaClass.scala)
Expected result:
whether compilation failure or no runtime error
NOTE: I have to use an intermediate java class in my Scala classes hierarchy to workaround some other compiler issues.
similar issues: https://github.com/scala/bug/issues/10534 https://github.com/scala/bug/issues/12224
Same in Scala 3.
These are valid issued, but I'm not sure if they will be considered high priority by core contributors any time soon.
NOTE: I have to use an intermediate java class in my Scala classes hierarchy to workaround some other compiler issues.
This sounds more important - is there a ticket for that?
is there a ticket for that
I am not sure, it's hard to create a minimal reproducible example. It is mainly related to Kotlin interop. We have some piece of library code, which is written in Kotlin and I didn't manage to use it in Scala. Sometimes scala compiler can't resolve an overloaded method from Kotlin which uses extensions as parameters (when Java can). Sometimes it tails with
java.lang.StackOverflowError
at scala.reflect.internal.tpe.TypeMaps$SubstMap.apply(TypeMaps.scala:754)
at scala.reflect.internal.tpe.TypeMaps$SubstSymMap.apply(TypeMaps.scala:820)
at scala.reflect.internal.Types$TypeRef.mapOver(Types.scala:2371)
at scala.reflect.internal.tpe.TypeMaps$SubstMap.apply(TypeMaps.scala:754)
at scala.reflect.internal.tpe.TypeMaps$SubstSymMap.apply(TypeMaps.scala:820)
at scala.reflect.internal.Types$TypeBounds.mapOver(Types.scala:1592)
at scala.reflect.internal.tpe.TypeMaps$SubstMap.apply(TypeMaps.scala:754)
at scala.reflect.internal.tpe.TypeMaps$SubstSymMap.apply(TypeMaps.scala:820)
at scala.reflect.internal.tpe.TypeMaps$SubstSymMap.apply(TypeMaps.scala:788)
at scala.reflect.internal.Symbols$Symbol.modifyInfo(Symbols.scala:1594)
at scala.reflect.internal.Symbols.$anonfun$cloneSymbols$2(Symbols.scala:3770)
at scala.reflect.internal.Symbols.cloneSymbols(Symbols.scala:3768)
at scala.reflect.internal.Symbols.cloneSymbols$(Symbols.scala:3764)
at scala.reflect.internal.SymbolTable.cloneSymbols(SymbolTable.scala:28)
at scala.reflect.internal.tpe.TypeMaps$TypeMap.mapOver(TypeMaps.scala:140)
at scala.reflect.internal.Types$ExistentialType.mapOver(Types.scala:3225)
at scala.reflect.internal.tpe.TypeMaps$SubstMap.apply(TypeMaps.scala:754)
at scala.reflect.internal.tpe.TypeMaps$SubstMap.apply(TypeMaps.scala:667)
at scala.reflect.internal.Types$TypeRef.mapOver(Types.scala:2380)
at scala.reflect.internal.tpe.TypeMaps$SubstMap.apply(TypeMaps.scala:754)
at scala.reflect.internal.Types$Type.subst(Types.scala:747)
at scala.reflect.internal.Types$Type.instantiateTypeParams(Types.scala:493)
at scala.reflect.internal.Types$TypeRef.seenFromOwnerInstantiated$1(Types.scala:2499)
at scala.reflect.internal.Types$TypeRef.relativize(Types.scala:2503)
at scala.reflect.internal.Types$TypeRef.$anonfun$baseTypeSeqImpl$2(Types.scala:2629)
at scala.reflect.internal.BaseTypeSeqs$BaseTypeSeq.map(BaseTypeSeqs.scala:157)
at scala.reflect.internal.Types$TypeRef.baseTypeSeqImpl(Types.scala:2629)
at scala.reflect.internal.Types.defineBaseTypeSeqOfTypeRef(Types.scala:2803)
at scala.reflect.internal.Types.defineBaseTypeSeqOfTypeRef$(Types.scala:2794)
at scala.reflect.internal.SymbolTable.defineBaseTypeSeqOfTypeRef(SymbolTable.scala:28)
at scala.reflect.internal.Types$TypeRef.baseTypeSeq(Types.scala:2636)
at scala.tools.nsc.typechecker.Implicits$ImplicitSearch.getClassParts$1(Implicits.scala:1348)
at scala.tools.nsc.typechecker.Implicits$ImplicitSearch.getParts$1(Implicits.scala:1378)
at scala.tools.nsc.typechecker.Implicits$ImplicitSearch.$anonfun$companionImplicitMap$12(Implicits.scala:1379)
at scala.tools.nsc.typechecker.Implicits$ImplicitSearch.getParts$1(Implicits.scala:1379)
at scala.tools.nsc.typechecker.Implicits$ImplicitSearch.getClassParts$1(Implicits.scala:1351)
at scala.tools.nsc.typechecker.Implicits$ImplicitSearch.getParts$1(Implicits.scala:1378)
at scala.tools.nsc.typechecker.Implicits$ImplicitSearch.$anonfun$companionImplicitMap$12(Implicits.scala:1379)
at scala.tools.nsc.typechecker.Implicits$ImplicitSearch.getParts$1(Implicits.scala:1379)
at scala.tools.nsc.typechecker.Implicits$ImplicitSearch.getClassParts$1(Implicits.scala:1351)
at scala.tools.nsc.typechecker.Implicits$ImplicitSearch.getParts$1(Implicits.scala:1378)
I don't think that it's worth reporting, taking into account that many Java-interop issues are not considered high priority.
looks like the exception is from https://github.com/scala/bug/issues/11775
println(Try(val2))
also AbstractMethodError when it extends Java class.
I'm interested in this question. Recently, I happened to read relevant issue. Let me explain my understanding. There may be some mistakes. I'm still a beginner.
Extends Java class
Combined with Scala code, the following may be a reasonable step.
- Implement
val2method - Implementation
sandbox$ScalaTrait$_setter_$val2_=method - emit
val2field - return
val2field byval2method - The static constructor of trait should be called, and constructor call
sandbox$ScalaTrait$_setter_$val2_=to setval2.
trait ScalaTrait {
val val2: String = "default val value 2"
}
object Test extends JavaAbstractClass {
def main(args: Array[String]): Unit = {
println(Try(javaVal2()))
}
}
abstract trait ScalaTrait extends Object {
<accessor> <sub_synth> protected[this] def sandbox$ScalaTrait$_setter_$val2_=(x$1: String): Unit;
<stable> <accessor> <sub_synth> def val2(): String;
def /*ScalaTrait*/$init$(): Unit = {
ScalaTrait.this.sandbox$ScalaTrait$_setter_$val2_=(("default val value 2": String));
()
}
};
object Test extends sandbox.JavaAbstractClass {
def main(args: Array[String]): Unit = scala.Predef.println(scala.util.Try.apply({
(() => Test.this.$anonfun$main$1())
}));
final <artifact> private[this] def $anonfun$main$1(): String = Test.javaVal2();
def <init>(): sandbox.Test.type = {
Test.super.<init>();
()
}
}
Not extends Java class
It's Ok
trait ScalaTrait {
val val2: String = "default val value 2"
}
object Test extends ScalaTrait {
def main(args: Array[String]): Unit = {
println(Try(val2))
}
}
abstract trait ScalaTrait extends Object {
<accessor> <sub_synth> protected[this] def sandbox$ScalaTrait$_setter_$val2_=(x$1: String): Unit;
<stable> <accessor> <sub_synth> def val2(): String;
def /*ScalaTrait*/$init$(): Unit = {
ScalaTrait.this.sandbox$ScalaTrait$_setter_$val2_=(("default val value 2": String));
()
}
};
object Test extends Object with sandbox.ScalaTrait {
override <stable> <accessor> def val2(): String = (Test.this.val2: String);
private[this] var val2: String = _;
override <accessor> protected[this] def sandbox$ScalaTrait$_setter_$val2_=(x$1: String): Unit = Test.this.val2 = (x$1: String);
def main(args: Array[String]): Unit = scala.Predef.println(scala.util.Try.apply({
(() => Test.this.$anonfun$main$1())
}));
final <artifact> private[this] def $anonfun$main$1(): String = Test.val2();
def <init>(): sandbox.Test.type = {
Test.super.<init>();
Test.super./*ScalaTrait*/$init$();
()
}
}