加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > 安全 > 正文

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

发布时间:2020-12-16 18:31:54 所属栏目:安全 来源:网络整理
导读:虽然我理解为什么var不能覆盖子类中的val,反之亦然,但我无法理解为什么 Scala不允许子类中的def覆盖超类中的var class Car { var age = 32}class SedanCar extends Car { override def age = 54} 因为var是可变的,为什么不允许def覆盖呢?有人可以帮我理解这
虽然我理解为什么var不能覆盖子类中的val,反之亦然,但我无法理解为什么 Scala不允许子类中的def覆盖超类中的var

class Car {
  var age = 32
}

class SedanCar extends Car {
  override def age = 54
}

因为var是可变的,为什么不允许def覆盖呢?有人可以帮我理解这个吗?

解决方法

这与 Liskov Substitution Principle有关:你不能分配 weaker access privileges in subclass (even for 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在覆盖def后变为不可变.它实际上违反了Uniform Access Principle,因为你必须看到你的var作为两个服务(读者和作者)而不是它实际上是一个,而UAP兼容性要求相反:读者和作者都应该用一个统一的表示法来表示.

附:实际上你的问题指向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_ =同时:

trait Car2 { var age = 100500 }
 class SeadanCarReadOnly extends Car2 { 
    override var age = 17; 
    override def age_: Int = 5 //should be compile error here
 }

但由于向后兼容性,很难在Scala语言中快速实现它

P.S./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 }

不管你可以:)

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读