Scala: смешивание черт с частными полями - PullRequest
7 голосов
/ 05 марта 2011

Вопрос не в этом, а скорее во мне, что это вообще возможно! Я написал этот небольшой пример, чтобы доказать обратное - я ожидал либо ошибку компилятора, либо одно из значений (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 s являются частными, но все же.

Ответы [ 3 ]

15 голосов
/ 05 марта 2011

Поскольку черты - это не просто интерфейсы, им нужен какой-то способ для хранения своего внутреннего состояния.Но они должны быть совместимы с интерфейсами - так что они делают?Они создают средства доступа для того, что выглядит как поле (как вы можете видеть (среди прочего) с 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 не искажено в этом примере.)

6 голосов
/ 05 марта 2011

Это свойство не относится к чертам характера. Например:

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. Частные члены частные . Они принадлежат исключительно туда, где они были определены, и не имеют никакого влияния снаружи. Если бы возник конфликт из-за наследования, они были бы видны, и это помешало бы быть личным.

1 голос
/ 05 марта 2011

Закрытые члены и методы не могут быть переопределены. Это должно предотвратить изменения в поведении класса, введенном потомками. Концепция здесь очень похожа на C ++ - каждый предок хранит свою собственную копию членов, пока не будут вызваны специальные методы (например, виртуальное наследование).

...