Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scala: Mixing traits with private fields

It's not much of a question, it's rather my excitement that it's possible at all! I wrote this little example just to prove the opposite - I expected either a compiler error or one of the values (111 or 222, I wasn't sure).

scala> trait T1 { private val v = 111; def getValueT1 = v }
scala> trait T2 { private val v = 222; def getValueT2 = v }
scala> class T12 extends T1 with T2                        
scala> val t = new T12                                     
scala> t.getValueT1                                        
res9: Int = 111
scala> t.getValueT2                                        
res10: Int = 222

Why doesn't the v get overridden? Off course this works only as long as vs are private, but still.

like image 655
Vilius Normantas Avatar asked Mar 05 '11 12:03

Vilius Normantas


2 Answers

Since traits are not just interfaces, they need some way to store their internal state. But they have to be compatible with interfaces--so what do they do? They create accessors for what looks like a field (as you can see (among other things) with javap -l -s -c -private on the class files):

public interface T1 extends java.lang.Object {
public abstract int T1$$v();
Signature: ()I

public abstract int getValueT1();
Signature: ()I
}

and then create an implementation class that has static methods to implement the functionality:

public abstract class T1$class extends java.lang.Object {
public static int getValueT1(T1);
  Signature: (LT1;)I
  Code:
   0:   aload_0
   1:   invokeinterface #12,  1; //InterfaceMethod T1.T1$$v:()I
   6:   ireturn
}

Now, hopefully, it's clear that these would be separate by default since these internally-generated methods have the name of the trait in the name of the method. And when we look in the implementation in T12:

public class T12 extends java.lang.Object implements T1,T2,scala.ScalaObject {
private final int Overridden$T1$$v;
  Signature: I

public final int T1$$v();
  Signature: ()I
  Code:
   0:   aload_0
   1:   getfield    #22; //Field T1$$v:I
   4:   ireturn

public int getValueT1();
  Signature: ()I
  Code:
   0:   aload_0
   1:   invokestatic    #29; //Method T1$class.getValueT1:(LT1;)I
   4:   ireturn
}

you can see that it just fills in what's needed for each specific trait. Now the question is--how do traits ever overwrite each other? They seem to be completely separate! That's the compiler's job. If something is private it's hidden and can't be overridden, so it doesn't matter that another (private) thing has the same name. But if it's not, the compiler complains of a collision:

error: overriding value v in trait T1 of type Int;
 value v in trait T2 of type Int needs `override' modifier
  class T12 extends T1 with T2

because now it's not using the secret mangled names with the trait name embedded. (Observe that getValueT1 is not mangled in this example.)

like image 110
Rex Kerr Avatar answered Nov 22 '22 06:11

Rex Kerr


This is not a property particular to traits. For instance:

scala> class X {
     |   private val v = 111
     |   def getX = v
     | }
defined class X

scala> class Y extends X {
     |   private val v = 222
     |   def getY = v
     | }
defined class Y

scala> new Y
res0: Y = Y@5ca801b0

scala> res0.getX
res1: Int = 111

scala> res0.getY
res2: Int = 222

And the same holds true in Java. Private members are private. They belong solely to where they were defined, and have no effect outside. If there was a conflict through inheritance, they would be visible, which would defeat the purpose of being private.

like image 41
Daniel C. Sobral Avatar answered Nov 22 '22 04:11

Daniel C. Sobral