-
-
Save ogirardot/4683919 to your computer and use it in GitHub Desktop.
object MyApp extends App { | |
val b = new B() | |
} | |
trait Conflictor { | |
val log = "reduction" | |
} | |
class A {} | |
object A extends Conflictor {} | |
class B extends A with Conflictor {} |
scala 2.10 ?
What do you mean "it won't run"? What would you expect? (edit: I use 2.10)
Something else ;)
[error] (run-main) java.lang.VerifyError: class B overrides final method log.()Ljava/lang/String;
java.lang.VerifyError: class B overrides final method log.()Ljava/lang/String;
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:791)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
at java.net.URLClassLoader.defineClass(URLClassLoader.java:449)
at java.net.URLClassLoader.access$100(URLClassLoader.java:71)
at java.net.URLClassLoader$1.run(URLClassLoader.java:361)
at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
at java.lang.ClassLoader.loadClass(ClassLoader.java:423)
at java.lang.ClassLoader.loadClass(ClassLoader.java:356)
at java.lang.Class.getDeclaredMethods0(Native Method)
at java.lang.Class.privateGetDeclaredMethods(Class.java:2442)
at java.lang.Class.getMethod0(Class.java:2685)
at java.lang.Class.getMethod(Class.java:1620)
I get a VerifyError at runtime: class B overrides final method log.()Ljava/lang/String;
Interesting..
Btw : It fails in scala 2.9.2, it works in scala 2.10.0. Interesting.
Apparently, in Scala 2.9.2, A
makes log
final. See javap for A
:
public class A extends java.lang.Object implements scala.ScalaObject{
public static final void Conflictor$_setter_$log_$eq(java.lang.String);
public static final java.lang.String log();
public A();
}
Hence the problem for B
.
This is an answer, but it may not be right :)
The object A is instantiated where defined. So it defines a val log on itself.
If I remember well, Scala compiles object A so that java code can call A.log (as a static value), so there should be a "log" member in class A calling log in A$ (am I right?)
So this could explain why B cannot extend A with Conflictor, because it tries to 'define' log a second time (there's already one in A)
yes but :
case class ahoy(s:String) {def apply(s:String) = ahoy(s + this.s)}
ahoy("a")("b") == ahoy("ba")
works fine. (there are a lot more collisions here)
So my idea is the compiler is avoiding collision in A
, but place unsafe static accessor for log if it can.
This one works :
object MyApp extends App {
val b = new B()
}
trait Conflictor {
val log = "reduction"
}
class A extends Conflictor {}
object A extends Conflictor {}
class B extends A with Conflictor {}
So, it compiles and won't run ... Hum hum ! Why ?