簡體   English   中英

為什么在Scala中用`def`覆蓋`var`是不可能的?

[英]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) 使vardef使得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所說,但這並不是那么明顯的解決方案,因為直觀地用戶期望varoverride 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 agedef 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM