为什么 Go 和 Rust 语言都舍弃了继承?
为什么go和rust语言都舍弃了继承?
❎ 舍弃了 Class
✅ 舍弃或弱化子类型
类的继承是一段儿弯路
OO 发明了继承,然后发现真正有意义的是 interface 的组合(更准确的说,是 Product type of interfaces or abstract classes),语义上相当于组合,但在 Cpp,Java 等语言中,还是使用继承来实现),具体类的继承于人的认知充满矛盾。
比如说,我们有一个类叫 “马”,可以构造除马的实例,这个类是具体的。然后,我们定义一个新的类 “白马 extends 马”,说明我们的程序世界里要开始考虑颜色了,此时,“马” 这个类实质上已经不再具体,因为不存在一批马,它的颜色是未定义。
进一步理解,马也好、白马也好,其实描述的是一种集合,后者是前者的子集。集合的唯一行为,是任给一个对象,可以判定它们是否在集合中。所以,白马,马这样的类型可以用做函数调用时的类型检查,并不能用作构造实例。它们就是 OO 中的 interface。
那么我们用什么来构造实例呢?一般我们称为构造型,或者许多 OO 语言里的构造函数,用以构造复合特定接口的对象,比如下面这一段代码
class 白马 implements 有毛色 {
public 白马 (String id) { ... }
public Color 毛色() { return 白色; }
}
表达的就是
interface 马 { ... }
interface 白马 extends 马, 有毛色 {...}
class 某具体马甲 implements 白马{
public 具体马甲(String id) { ... }
public Color 毛色() { return 白色;}
}
如果未来又有 “白色蒙古马”
完全可以定义为:
interface 马 { ... }
interface 白马 extends 马, 有毛色 {...}
interface 白色蒙古马 extends 白马, 有产地 { ... }
class 某具体马乙 implements 白色蒙古马{
public 某具体马乙(String id) { ... }
public Color 毛色() { return 白色; }
public String 产地() { return 蒙古; }
}
有小伙伴可能会问,你这里马甲,马乙不还是一个没具体产地,一个有具体产地马?是的,但它两是完全无关的构造型,不存在马甲 isa 马乙,或者马乙 isa 马甲的关系,谁都不比谁更具体,也就谁不比谁更抽象,马乙有产地,并不会让马甲更抽象。我们完全还可以定义
class 某具体马丙 implements 马, 有产地{
public 某具体马丙(String id) { ... }
public String 产地() { return 蒙古; }
}
这大概就是我们不需要类的继承的原因吧。
至于接口的继承,本质上就是已有接口和新定义的行为构成的匿名接口的组合。两个接口的组合,其实就是两者行为的并集,表达两大接口约束对象的交集。