[英]Why it's impossible to override `var` with `def` in Scala?
雖然我理解為什么var
不能覆蓋子類中的val
,反之亦然,但我無法理解為什么Scala不允許子類中的def
覆蓋超類中的var
class Car {
var age = 32
}
class SedanCar extends Car {
override def age = 54
}
因為var
是可變的,為什么不允許def
覆蓋呢? 有人可以幫我理解這個嗎?
這與Liskov替換原則有關 :您不能在子類中分配較弱的訪問權限(即使對於Java) 。 使var
為def
使得setter def x_= (y: T ): Unit
private(如@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可以僅覆蓋變量的getter部分,如@RégisJean-Gilles所說,但這並不是那么明顯的解決方案,因為直觀地用戶期望var
在override def
后變為不可變。 它實際上違反了統一訪問原則,因為你必須將你的var
視為兩個服務(讀者和作者)而不是它實際上的一個,而UAP兼容性要求相反:讀者和作者都應該用一個統一的符號來表示。
PS其實你的問題指向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
對於cource,這種方法應該禁用覆蓋var age
和def age_; def 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 }
不管你可以:)
我認為你對這個概念有疑問。 來自Scala參考:
變量聲明var x:T等同於getter函數x和setter函數x_ =的聲明,定義如下:
def x: T
def x_= (y: T ): Unit
所以你試圖用“年齡= 54”來覆蓋一個吸氣劑。 但是現在你對setter沒有任何用處。
希望你明白我的意思。 我認為為什么人們“減去”你的問題是因為你不是在思考Scala的思維方式。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.