unn*_*ddr 8 inheritance struct interface embedding go
我在Go写一个解释器,我正在寻找存储AST的惯用方法.我读了Go编译器源代码,似乎他们使用带有空方法的接口来表示AST.例如,我们有以下层次结构,
Object
--Immovable
----Building
----Mountain
--Movable
----Car
----Bike
Run Code Online (Sandbox Code Playgroud)
这就是上述层次结构以"空方法"的方式实现的方式.
type Object interface {
object()
}
type Immovable interface {
Object
immovable()
}
type Building struct {
...
}
type Mountain struct {
...
}
type Movable interface {
Object
movable()
}
type Car struct {
...
}
type Mountain struct {
...
}
func (*Building) object() {}
func (*Mountain) object() {}
func (*Car) object() {}
func (*Bike) object() {}
func (*Building) immovable() {}
func (*Mountain) immovable() {}
func (*Car) movable() {}
func (*Bike) movable() {}
Run Code Online (Sandbox Code Playgroud)
上面的代码是一个人为的例子,这就是Go编译器用几十个空方法实现 AST的方式.但为什么?请注意定义了多少个空方法.随着层次深度的增加,它可能变得非常复杂.
评论中指出,空方法不允许分配不兼容的类型.在我们的示例中,a *Car不能分配给*Immovable例如.
在其他语言(例如支持继承的C++)中,这很容易.我想不出任何其他代表AST的方式.
Go编译器AST的实现方式可能是惯用的,但是不是不那么简单吗?
Go 不是(完全)一种面向对象的语言:它没有类,也没有类型继承 ; 但是它支持一个类似的构造,在层次和层次上都称为嵌入,它确实有方法.structinterface
Go中的接口只是固定的方法集.如果接口的方法集是接口的超集(没有intent的声明),则类型隐式地实现接口.
如果要明确记录或明确说明您的类型实现了接口(因为没有明确说明),那么空方法很棒.官方围棋常见问题解答:我怎样才能保证我的类型满足界面?
type Fooer interface {
Foo()
ImplementsFooer()
}
Run Code Online (Sandbox Code Playgroud)
如果你想在你的类型层次区分(例如,你不想让一个对象既Movable和Immovable),它们必须有不同的方法集(必须有至少1方法在每个方法集的Movable和Immovable是不存在于另一个的),因为如果方法集包含相同的方法,一个的实现也会自动实现另一个,因此你可以将一个Movable对象分配给一个类型的变量Immovable.
将空方法添加到具有相同名称的接口将为您提供此区别,假设您不会将此类方法添加到其他类型.
就个人而言,我对空方法没有任何问题.有一种方法可以减少它们.
如果您还为层次结构中的每个类型创建了一个struct 实现,并且每个实现都将实现嵌入了struct一个更高的级别,那么更高级别的方法集将自动生成:
Object界面和ObjectImpl实现:
type Object interface {
object()
}
type ObjectImpl struct {}
func (o *ObjectImpl) object() {}
Run Code Online (Sandbox Code Playgroud)
Immovable界面和ImmovableImpl实现:
type Immovable interface {
Object
immovable()
}
type ImmovableImpl struct {
ObjectImpl // Embed ObjectImpl
}
func (o *Immovable) immovable() {}
Run Code Online (Sandbox Code Playgroud)
注意ImmovableImpl只添加immovable()方法,object()是"继承".
Building 执行:
type Building struct {
ImmovableImpl // Embed ImmovableImpl struct
// Building-specific other fields may come here
}
Run Code Online (Sandbox Code Playgroud)
注意Building 不添加任何新方法,但它自动成为Immovable对象.
如果"子类型"的数量增加或者接口类型具有多于1个"标记"方法(因为所有方法都是"继承的"),则此技术的优势会大大增加.