Это связано с Liskov Substitution Principle: вы не можете назначить weaker access privileges in subclass (even for Java). Изготовление var
a def
делает сеттер def x_= (y: T): Unit
частным (как сказал @Staix). Поэтому в случае, если Seadan Car
имеет формальный тип - к нему не следует обращаться, но компилятор не может найти такие случаи вообще (только формальные типы известны во время компиляции), поэтому такое поведение отключается, как любая более слабая привилегия:
val car2 = new SeadanCar
car.age = 422 //compiler error, can't mutate "def age"
val car: Car = new SeadanCar
car.age = 42 //now you had mutated immutable
Точка принципа замещения Принцип поведения не должен изменяться после литья в супертип.
С другой стороны, может переопределить Scala только геттер часть переменной, как @ Режис Жан-Жиль сказал, но это не так очевидно, решение, потому что интуитивно пользователь ожидает var
становится непреложным после override def
. И это фактически нарушает Uniform Access Principle, поскольку вы должны видеть свой var
как две услуги (читатель и писатель), а не та, что есть на самом деле, в то время как UAP-compatibilty требует обратного: и читатель, и писатель должны быть представлены единой унифицированной нотацией.
P.S. На самом деле ваш вопрос указывает на незавершенность UAP-совместимости Scala. Как я уже сказал, переопределение только считывателя var
и оставление записи как есть будет несовместимым с UAP - это блокировка способности переопределять var
(так что вы не можете переопределить var в обоих направлениях: как в вычислительном, так и в хранилище), даже несмотря на то, что вы уже не можете переопределить его из-за LSP. Но текущее решение scala также проблематично. Вы можете обнаружить, что вы можете:
trait Car { def age: Int = 7; def age_=(a: Int) = {}}
class SeadanCar extends Car { override def age: Int = 5}
Но не может
// just repeating your example
trait Car { var age: Int = 7 }
class SeadanCar extends Car { override def age: Int = 5}
Так наследование в Scala, кажется, не совместимы с UAP. ИМХО, большая проблема заключается в том, что читатель и сам var имеют одинаковые имена, поэтому вы не можете их отличить (при определении, а не при доступе).Я бы решить ее с чем-то вроде:
trait Car { def age_: Int = 7; def age_=(a: Int) = {}}
class SeadanCarReadOnly extends Car { override def age: Int = 5} //can't compile as reader is closed
class SeadanCarVar extends Car { override var age: Int = 5}
class SeadanCarReadOnly extends Car { override def age_: Int = 5}
trait Car2 { var age = 100500 }
class SeadanCarReadOnly extends Car2 { override def age_: Int = 5}
Обратите внимание, что переопределение age_
в моем предлагаемом примере должно привести к:
scalaxx> (new SeadanCarReadOnly).age //call age_ here
resxx: Int = 5
scalaxx> (new SeadanCarReadOnly).age_
resxx: Int = 5
Не нравится:
trait Car2 { @BeanProperty var age = 100500 }
class SeadanCarReadOnly extends Car2 { override def getAge: Int = 5}
//which leads to inconsistency:
scala> (new SedanCar()).age
res6: Int = 30
scala> (new SedanCar()).getAge
res7: Int = 54
из задних, такие подход должен отключать переопределение var age
и def age_; def age_=
одновременно:
trait Car2 { var age = 100500 }
class SeadanCarReadOnly extends Car2 {
override var age = 17;
override def age_: Int = 5 //should be compile error here
}
, но это трудно быстро реализовать его в Scala языке из-за обратной совместимости
PS/2 Просто говоря, о изменяемости/immutabilty части вопроса, то вы определенно не можете сделать это (в связи с LSP):
trait Car { var age: Int = 32 } //or without initial value
class SedanCar extends Car { override val age = 42 }
И, в связи с LSP + UAP, не должны быть в состоянии сделать это:
trait Car { def age: Int = 7; def age_=(a: Int) = {}}
class SedanCar extends Car { override val age = 42 }
regardl что вы можете:
Поскольку defs не являются изменяемыми переменными, но определениями методов. Что вы хотите, если вы делаете «age = 32» в SedanCar? –
Я думаю, что вопрос ОФ вполне резонный. Скала часто представлена в качестве принципа равномерного доступа, по уважительной причине. Тогда было бы неразумно рассматривать определение «age» var как ничто иное, как определение пары getter и seters (методы «age» и «age_ =»). В этом случае я определенно могу переопределить «возраст», который фактически перекрыл бы только геттер и оставил установщик как есть. –