继承

继承,放到三次元就是说长辈的东西留给子辈,在编程中的继承也差不多。

请再回忆起之前讲解面向对象时用过的例子:手机。

我们当时说过,手机有颜色、电量这些属性,还有开机、打电话这种方法,用我们现有的知识,我们可以把手机写成下面这种类:

class_name 手机

# 默认是个黑色的手机,后面的三个零分别代表红绿蓝三色值,范围是 0 到 1
var 颜色: Color = Color(0, 0, 0)

# 默认满电,且电量变量取值范围是 0 ~ 100
var 电量: int = 100 : 
    set(新电量):
        if 新电量 > 100: 新电量 = 100
        elif 新电量 < 0: 新电量 = 0
        电量 = 新电量

var 已经开机 := false

func 开机():
    print("加载中...")
    已经开机 = true
    print("开机完成")

func 打电话(电话号码:String):
    if 已经开机:
        print("给 " + 电话号码 + " 打了电话")

这个手机挺好用的,能开机能打电话,但是某天国外一小伙敲不死推出了一款新手机,竟然能发短信:

func 发短信(电话号码:String, 短信内容:String):
    if 已经开机:
        print("给 " + 电话号码 + " 发信息,内容如下:" + 短信内容)

直接添加这个方法会导致每个手机实例都支持发短信方法,然而实际上,并不是每款手机都能发短信,因此,我们需要区分这个手机实例是老款手机还是新款手机。目前这样简单的功能我们可以添加一个属性来表示手机型号,并在新功能中判断该手机是否是新型,但如果新功能很多,这样的粗暴解决方式就不太好用了。最终的解决方案是,我们希望游戏中有两个类,一个是手机类,能发短信,另一个也是手机类,但不能发短信。

如果你是个勤劳者,现在只需要把上面代码复制一遍,再写一个包含 发短信 方法的新手机类即可,这样我们的游戏中就拥有了两种手机。

不过,历史上那些创造编程语言的人可能并不勤劳,但好在他们都很聪明,为了不写重复的代码,他们发明了继承机制,可以在某个类的基础上作出修改以创造新的类,这种情况下,我们把原有的类称为 基类父类,修改出来的新的类称为 派生类子类

偷懒是进步的阶梯,我们从来不写重复的代码。

继承的核心思想就是把父类的东西传承给子类,也就是说,父类有的东西子类也都有,但子类有的父类不一定有。上面的手机就是很好的例子,老款手机就是父类,新款手机就是子类,老手机能做的事情新手机也都能做,但新手机能发短信,老手机则不行。

现在我们动手来写新手机:

extends 手机
class_name 能发短信的手机

func 发短信(电话号码:String, 短信内容:String):
    if 已经开机:
        print("给 " + 电话号码 + " 发信息,内容如下:" + 短信内容)

ok,很简单就写完了,我们使用一个 extends 关键字指定了当前类的父类,也就是告诉 GDScript 当前类是根据 手机 类修改而来,因此我们不用再次书写颜色、电量等属性或方法,只需要添加新功能即可。

[!note]

GDScript 仅支持单继承,就是说子类只能继承自一个父类。

不过,父类还可能继承自另一个父类(爷爷类),所以继承关系可以形成一条长链。