Это связано с принципом подстановки Лисков: вы не можете назначать более слабые права доступа в подклассе (даже для Java). Преобразование var
в def
делает сеттер def x_= (y: T ): Unit
закрытым (как сказал @Staix). Таким образом, в случае, когда Seadan Car
имеет формальный тип 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 может переопределить только геттерную часть переменной, как сказал @Régis Jean-Gilles, но это не столь очевидное решение, потому что интуитивно пользователь ожидает, что var
станет неизменным после override def
. И это на самом деле нарушает принцип единого доступа, поскольку вы должны рассматривать var
как две службы (читатель и писатель) вместо одного, а UAP-совместимость требует обратного: и читатель, и писатель должны быть представлены одной унифицированной нотацией.
P.S. На самом деле ваш вопрос указывает на неполноту совместимости scala с UAP. Как я уже сказал, переопределение только считывателя 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 Просто отметим, что в части изменчивости/неизменяемости вопроса вы определенно не можете этого сделать (из-за 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 }
несмотря на то, что ты можешь :)
person
dk14
schedule
23.12.2014
age = 32
в SedanCar? - person The Archetypal Paul   schedule 23.12.2014age
var как не что иное, как определение пары методов получения и установки (age
иage_=
). В этом случае я определенно должен иметь возможность переопределитьage
, что фактически переопределит только геттер и оставит сеттер как есть. - person Régis Jean-Gilles   schedule 23.12.2014