5

vara がサブクラスでa をオーバーライドできない理由、valおよびその逆ができない理由は理解していますが、Scaladefがサブクラスで avarをスーパークラスでオーバーライドできない理由を理解できません。

class Car {
  var age = 32
}

class SedanCar extends Car {
  override def age = 54
}

変更可能であるため、 a がそれをオーバーライドvarできないのはなぜですか? def誰でもこれを理解するのを手伝ってもらえますか?

4

2 に答える 2

12

これは、 Liskov Substitution Principleに関連しています。サブクラスで弱いアクセス権を割り当てることはできません(Java の場合でも) 。varaを作成するdefと、セッターがdef x_= (y: T ): Unitプライベートになります(@Staixが言ったように)。そのため、 whenSeadan 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

Substitutability Principle のポイントは、スーパータイプへのキャスト後に動作を変更してはならないということです。

一方、@Régis Jean-Gillesが言ったように、scalaは変数のゲッター部分のみをオーバーライドできますが、直感的にユーザーはvar後で不変になることを期待しているため、これはそれほど明白な解決策ではありませんoverride def。そして、実際には 1 つのサービスではなく 2 つのサービス (リーダーとライター) として見なす必要があるため、Uniform Access Principleに違反していますが、UAP 互換性にはその逆が必要です。つまり、リーダーとライターの両方を 1 つの統一表記で表す必要があります。var

PS 実際、あなたの質問は、scala の UAP 互換性が不完全であることを示しています。私が言ったように、varのリーダーのみをオーバーライドし、ライターをそのままにしておくと、UAP と矛盾し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 と互換性がないようです。IMHO、大きな問題は、リーダーと 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 agedef 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 }

あなたができるという事実に関係なく:)

于 2014-12-23T17:05:51.517 に答える
0

コンセプトに問題があると思います。Scala リファレンスから:

変数宣言 var x: T は、次のように定義された getter 関数 x および setter 関数 x_= の宣言と同等です。

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

したがって、「age = 54」でゲッターをオーバーライドしようとしています。しかし、今ではセッターを使用する必要はありません。

私が言いたいことを理解していただければ幸いです。人々があなたの質問を「差し引いた」理由は、ここで Scala の考え方を考えていないからだと思います。

于 2014-12-23T16:03:28.150 に答える