从多态角度,理解kotlin的继承和重写

昨天刚开始看kotlin,目前过到了类和对象这块的语法这块内容,在kotlin成员重写包括两种:类属性重写和类方法重写,在类属性重写中看到文档中说val无法重写var的成员变量,我实在菜鸟教程看的,原文如下:

你可以用一个var属性重写一个val属性,但是反过来不行。因为val属性本身定义了getter方法,重写为var属性会在衍生类中额外声明一个setter方法

不知道你们看完这个是怎么想的,我看完之后的感觉是:后面解释和前面的问题不搭边。明明说的是不能将var属性重写为val,怎么说的好像是将val属性重写为var属性出现的问题。。。。。。

之后跟java语言做了一下类比,当时在学java的时候有这么一个说法:子类的范围要>=父类的范围。或者说,从父类继承的方法,子类的访问类型只能更大。

最开始学java的时候没有考虑到这个问题,现在对比着kotlin仔细想一下是可以这么理解,我举个简单的栗子:

class Father{
    
    protected void f(){
        
    }
}

class Child extends Father{
    @Override
    protected void f() {
        super.f();
    }
}

这是再简单不过的一段代码,我们稍微改一下:

class Father{
    
    protected void f(){
        
    }
}

class Child extends Father{
    @Override
    public void f() {
        super.f();
    }
}

这个也是没有问题的,再接着看:

class Father {

    protected void f() {

    }
}

class Child extends Father {
    @Override
    void f() {//报错了,这里
        super.f();
    }
}

这里就会报错了,因为之前父类的访问类型为protected,第一段代码的访问类型是protected,第二个访问类型是public,都是>=父类的访问类型。

而第三个默认访问类型是要比protected访问类型范围小,至于private大家可以自己试一下,也是错误的。


一开始学的时候只是没有尝试过修改复写方法的访问类型,只是跟父类的一样,现在思考一下为什么改为更大的访问类型,还是正确的?

我是这样思考的:子类的访问类型要比父类大,换句话说:使用父类能访问的,子类都能访问,可能有的同学这句话听得有点懵,这个其实就是面向对象的三大特性中的——多态,用父类引用访问子类对象,敲个代码就理解了:

public class GFZY {
    public static void main(String[] args) {
        Father child = new Child();
        child.f();
    }
}

class Father {

    protected void f() {

    }
}

class Child extends Father {
    @Override
    public void f() {
        super.f();
    }
}

看到这里相信大家已经知道我想说什么了:如果说子类复写的访问类型要比父类小,那使用父类引用访问子类方法的时候就可能会出问题啊!!!!

在上述栗子中,你把子类复写的方法访问类型设置为private或者default的话,使用父类引用的话依旧可以访问这个方法,但实际上他已经是私有的方法了,这样不就自相矛盾了吗。


看完上面这个案例,我们顺着这个思路,思考一下kotlin中的实现:

在kotlin中复写成员属性时,子类无法将父类的var类型属性复写为val,假如可以这样复写,说明在子类中并没有setXXX的属性方法。

package kt

open class Student {

    internal open var store: Int = 0

    internal open var s: String = ""

    constructor(num: Int, store: Int) : super() {
        this@Student.store = store
        println("构造函数输出   $num")
    }

}

class HighStudent(var asd: String) : Student(100, 3) {

    override val s: String = ""//这是个错的,这是当案例用的,,,注意一下

}

那这时候使用父类引用就可以给子类属性赋值了!!!!!!但是实际上子类已经是val类型的,不能重复赋值。

fun main(args: Array<String>) {

    var highStudent: Student = HighStudent("tom")
    highStudent.s = "我没法赋值,,,,"

}

而且从var、val的性质来看,var的范围要比val大,因为在var要比val多了一个setter函数

本质上还是可以理解为子类复写的范围要>=父类(用多态进行反证)

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值