这不是一个问题,而是我的兴奋,它是可能的!我写这个小例子只是为了证明相反 - 我预计编译器错误或其中一个值(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
是私有的,但这仍然有效。
答案 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 ++ - 每个祖先都拥有自己的成员副本,直到调用特殊方法(例如虚拟继承)。