Skip to content

Instantly share code, notes, and snippets.

@ogirardot
Last active December 12, 2015 00:28
Show Gist options
  • Save ogirardot/4683919 to your computer and use it in GitHub Desktop.
Save ogirardot/4683919 to your computer and use it in GitHub Desktop.
Simple gist to show problem when using companion object extending trait and sub-class using the same trait
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 {}
@ahoy-jon
Copy link

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)

@gourlaysama
Copy link

I get a VerifyError at runtime: class B overrides final method log.()Ljava/lang/String;

Interesting..

@ahoy-jon
Copy link

Btw : It fails in scala 2.9.2, it works in scala 2.10.0. Interesting.

@gourlaysama
Copy link

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.

@rsertelon
Copy link

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)

@ahoy-jon
Copy link

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.

@ahoy-jon
Copy link

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 {}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment