Scala:将特征与私人领域混合

时间:2011-03-05 12:26:55

标签: scala multiple-inheritance traits

这不是一个问题,而是我的兴奋,它是可能的!我写这个小例子只是为了证明相反 - 我预计编译器错误或其中一个值(111或222,我不确定)。

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

为什么v没有被覆盖?当然,只有v是私有的,但这仍然有效。

3 个答案:

答案 0 :(得分:15)

由于traits不仅仅是接口,它们还需要一些方法来存储它们的内部状态。但它们必须与接口兼容 - 那么他们做了什么?他们为看起来像字段的东西创建访问器(正如你可以在类文件中看到javap -l -s -c -private那样):

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

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

然后创建一个实现类,该实现类具有实现该功能的静态方法:

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
}

现在,希望很明显这些内容默认是分开的,因为这些内部生成的方法在方法名称中具有特征的名称。当我们查看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
}

你可以看到它只是填补了每个特定特征所需的内容。现在的问题是 - 特征永远如何相互覆盖?他们似乎是完全分开的!这是编译器的工作。如果某个东西是private它隐藏了并且无法被覆盖,那么另一个(私有)东西具有相同的名称并不重要。但如果不是,编译器会抱怨发生冲突:

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

因为现在它没有使用嵌入了特征名称的秘密错位名称。 (注意在此示例中getValueT1未被破坏。)

答案 1 :(得分:6)

这不是特质特有的属性。例如:

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

在Java中也是如此。私人会员私人。它们只属于它们被定义的地方,并且在外面没有任何影响。如果通过继承存在冲突,那么它们将是可见的,这将破坏私有化的目的。

答案 2 :(得分:1)

无法覆盖私有成员和方法。它是为了防止后代注入的类行为的变化。这里的概念非常类似于C ++ - 每个祖先都拥有自己的成员副本,直到调用特殊方法(例如虚拟继承)。