2014-12-23 3 views
5

Хотя я понимаю, почему var не может переопределить в подклассе, и наоборот в val, я не могу понять, почему Scala не позволяют def в подклассе переопределить var в суперклассаПочему нельзя переопределять `var` с` def` в Scala?

class Car { 
    var age = 32 
} 

class SedanCar extends Car { 
    override def age = 54 
} 

Как var изменчива, почему не разрешить def переопределить его? Может ли кто-нибудь помочь мне в понимании этого?

+1

Поскольку defs не являются изменяемыми переменными, но определениями методов. Что вы хотите, если вы делаете «age = 32» в SedanCar? –

+5

Я думаю, что вопрос ОФ вполне резонный. Скала часто представлена ​​в качестве принципа равномерного доступа, по уважительной причине. Тогда было бы неразумно рассматривать определение «age» var как ничто иное, как определение пары getter и seters (методы «age» и «age_ =»). В этом случае я определенно могу переопределить «возраст», который фактически перекрыл бы только геттер и оставил установщик как есть. –

ответ

12

Это связано с 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 что вы можете:

+1

Я могу купить «то, чего ждут пользователи», но я почтительно не согласен с тем, что переопределение только геттера нарушит UAP. В конце концов, при этом вы все равно можете читать или писать «возраст», ссылаясь только на «возраст». Я не вижу, как это могло бы быть более «единой нотации». Я могу представить причины этого ограничения, но нарушение UAP не является одним из них, совершенно наоборот. –

+0

В качестве побочного примечания мне на самом деле не очень важно, чтобы никто не мог переопределить получателя. Но на самом деле вы не можете переопределить var вообще, и это один облом. –

+0

1) 'var' - унифицированная нотация,' get' + 'set' - нет. когда вы говорите «давайте переопределяем только получить!» - вы, очевидно, предпочитаете второй и подталкиваете пользователя думать о геттерах/сеттерах. 2) Я думаю, что мой пример с формальным типом представлен достаточно, чтобы понять, почему вы не можете переопределить var «вообще». – dk14

0

Я думаю, что у вас есть проблема с концепцией. От Scala ссылок:

Объявления переменной Var х: Т эквивалентен декларации функции геттерных х и функция сеттера x_ =, определяются как следующим образом:

def x: T 
def x_= (y: T): Unit 

Таким образом, вы пытаетесь переопределить геттер «возраст = 54». Но теперь у вас нет никакой пользы для сеттера.

Надеюсь, вы поняли, что я имею в виду. Я думаю, почему люди «минус» ваш вопрос, потому что вы не думаете в мышлении Scala здесь.

+4

Ваш ответ точно указывает на суть проблемы: 'var x: T' должен быть эквивалентен' def x: T; def x_ = (y: T): Unit', но это не так. С последним определением я полностью имею право переопределять только геттер, оставляя сеттер как есть. Это неверно с определением var. –

+3

Hahahahaha, еще одна ошибка спецификации Scala. Ухоженная! – gzm0

+0

@ RégisJean-Gilles декларация не является определением. –