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

 姿萱俊达俊宏 发布于 2022-12-04 02:05

虽然我理解为什么a var不能覆盖valin子类,反之亦然,但我无法理解为什么Scala不允许defin子类覆盖var超类中的

class Car {
  var age = 32
}

class SedanCar extends Car {
  override def age = 54
}

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

1 个回答
  • 这与Liskov替换原则有关:您不能在子类中分配较弱的访问权限(即使对于Java).制作var一个def使得setter def x_= (y: T ): Unit私有(如@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 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 }
    

    不管你可以:)

    2022-12-11 02:07 回答
撰写答案
今天,你开发时遇到什么问题呢?
立即提问
热门标签
PHP1.CN | 中国最专业的PHP中文社区 | PNG素材下载 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有