これは、 Liskov Substitution Principleに関連しています。サブクラスで弱いアクセス権を割り当てることはできません(Java の場合でも) 。var
aを作成する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 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 }
あなたができるという事実に関係なく:)