【问题标题】:Scala: Mixing traits with private fieldsScala:将特征与私有字段混合
【发布时间】:2011-03-05 12:26:54
【问题描述】:

这不是什么大问题,我很兴奋,这完全有可能!我写这个小例子只是为了证明相反的情况 - 我预计编译器错误或其中一个值(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 不被覆盖?当然,这仅适用于 vs 是私人的,但仍然有效。

【问题讨论】:

    标签: scala multiple-inheritance traits


    【解决方案1】:

    由于特征不仅仅是接口,它们需要某种方式来存储其内部状态。但是它们必须与接口兼容——那么它们是做什么的呢?他们为看起来像一个字段的内容创建访问器(您可以在类文件中看到(除其他外)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
    }
    

    您可以看到它只是填充了每个特定特征所需的内容。现在的问题是——特征 ever 如何相互覆盖?他们似乎完全分开了!那是编译器的工作。如果某个东西是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 在此示例中未损坏。)

    【讨论】:

    • 不错的答案。稍作修正 - T1$class 称为实现类。
    • 确实——伴生类是另外一回事。固定。
    【解决方案2】:

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

    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 也是如此。私有成员是私有的。它们只属于它们被定义的地方,在外面没有影响。如果通过继承发生冲突,它们将是可见的,这会破坏私有的目的。

    【讨论】:

    • 现在我为这样一件微不足道的事情感到如此兴奋有点尴尬 :) 我想我只是太专注于特征而无法看到更广阔的前景。
    • @Vilius 好吧,从 Scala 方面来看,它看起来微不足道。当一个来自充满限制的Java时,它看起来确实很特别。
    【解决方案3】:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2010-11-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-05-09
      • 2020-12-19
      相关资源
      最近更新 更多