接口只是固定的方法集。如果类型隐式地实现了一个接口,如果它的方法集是接口的超集(没有意图声明)。
空方法如果你想显式地 或状态,你的类型确实实现了一个接口(因为它没有明确说明)。官方
type Fooer接口{
Foo()
ImplementsFooer()
}
$ c $如果你想在你的类型层次结构中进行区分(例如,你不希望允许一个对象既可以是可移动的 和 Immovable ),它们必须有不同的方法集(每个 Movable 和 Immovable ),因为如果方法集包含相同的方法,则实现一个也可以实现另一个,因此你可以为 Immovable 类型的变量赋一个 Movable 对象。
将一个空方法添加到具有相同名称的接口将为您提供这种区别,假设您w不要将这样的方法添加到其他类型中。
减少空方法的数量
我个人没有问题无论用什么方法。有一种方法可以减少它们。
如果您还创建了 struct 实现对于层次结构中的每种类型,每个实现将 struct 实现嵌入更高一级的水平,高一级的方法集将自动进入ado:
对象
对象界面和 ObjectImpl 实现:
类型对象接口{
object()
类型ObjectImpl struct {}
func(o * ObjectImpl)object(){}
不可移动
不可移动界面和 ImmovableImpl $
type不可移植界面{
Object
immovable()
}
type ImmovableImpl struct {
ObjectImpl // Embed ObjectImpl
}
func(o * Immovable)immovable(){}
请注意 ImmovableImpl 仅添加 immovable()方法, object()是继承的 。
建筑物
建筑物执行:
类型构建结构{
ImmovableImpl //嵌入ImmovableImpl结构
//特定于建筑物的其他字段可能来自这里
code
$ b $ p $注意 Building 不添加任何新方法,但它自动为不可移动对象。
如果子类型的数量增加或者接口类型不只有1个标记方法(因为所有方法都是继承的),则此技术的优势会大大增加。 )。
I am writing an interpreter in Go and I am looking for the idiomatic way to store the AST. I read the Go compiler source code and it seems they used interfaces with an empty method to represent the AST. For example, we have the following hierarchy,
Object --Immovable ----Building ----Mountain --Movable ----Car ----BikeThis is how the above hierarchy is implemented in the "empty method" way.
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() {}The above code is a contrived example and this is how the Go compiler implemented the AST with dozens of empty methods. But WHY? Note how many empty methods are defined. It may get very complicated with the increase of the depth of the hierarchy.
It is stated in the comments that the empty methods disallow the assignment of incompatible types. In our example, a *Car can't be assigned to a *Immovable for instance.
This is so easy in other languages like C++ that supports inheritance. I can't think of any other way of representing the AST.
The way how the Go compiler AST is implemented may be idiomatic but isn't it less straight forward?
解决方案Go is not (quite) an object oriented language: it does not have classes and it does not have type inheritance; but it supports a similar construct called embedding both on struct level and on interface level, and it does have methods.
Interfaces in Go are just fixed method sets. A type implicitly implements an interface if its method set is a superset of the interface (there is no declaration of the intent).
Empty methods are great if you want to document or state explicitly that your type does implement an interface (because it is not stated explicitly). Official Go FAQ: How can I guarantee my type satisfies an interface?
type Fooer interface { Foo() ImplementsFooer() }If you want a distinction in your type hierarchy (e.g. you don't want to allow an object to be both Movable and Immovable), they must have different method sets (there must be at least 1 method in each of the method sets of Movable and Immovable that is not present in the other's), because if the method sets would contain the same methods, an implementation of one would automatically implement the other too therefore you could assign a Movable object to a variable of type Immovable.
Adding an empty method to the interface with the same name will provide you this distinction, assuming that you will not add such methods to other types.
Reducing the number of empty methodsPersonally I have no problem with empty methods whatsoever. There is a way to reduce them though.
If you also create a struct implementation for each type in the hierarchy and each implementation embeds the struct implementation one level higher, the method set of one level higher will automatically come without further ado:
ObjectObject interface and ObjectImpl implementation:
type Object interface { object() } type ObjectImpl struct {} func (o *ObjectImpl) object() {}Immovable
Immovable interface and ImmovableImpl implementation:
type Immovable interface { Object immovable() } type ImmovableImpl struct { ObjectImpl // Embed ObjectImpl } func (o *Immovable) immovable() {}Note ImmovableImpl only adds immovable() method, object() is "inherited".
BuildingBuilding implementation:
type Building struct { ImmovableImpl // Embed ImmovableImpl struct // Building-specific other fields may come here }Note Building does not add any new methods yet it is automatically an Immovable object.
The advantage of this technic grows greatly if the number of "subtypes" increases or if the interface types have more than just 1 "marker" method (because all methods are "inherited").
这篇关于Go创建复杂的结构层次结构的惯用方法是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!