正如VonC所写,但我想指出一些事情.
所述脆弱的基类问题通常归咎于虚拟方法(方法动态调度 -这意味着如果方法可重写时,其具有的实际实现在这样的重写方法只能在运行时被决定的情况下被调用).
为什么这是个问题?你有一个类,你添加一些方法,如果MethodA()调用MethodB(),你不能保证MethodB()你所写的将被调用,而不是一个子类的其他方法覆盖你的MethodB().
在Go中有嵌入,但没有多态性.如果在结构中嵌入类型,则嵌入类型的所有方法都将被提升,并且将位于包装器结构类型的方法集中.但是你无法"覆盖"推广的方法.当然,您可以使用相同的名称添加自己的方法,并在包装器结构上调用该名称的方法将调用您的方法,但是如果从嵌入类型调用此方法,则不会将该方法调度到您的方法,仍然会调用为嵌入式定义的"原始"方法.
因此,我认为脆弱的基类问题只在Go中以一种相当缓和的形式存在.
我们来看一个例子吧.首先是Java,因为Java"遭受"这种问题.让我们创建一个简单的Counter类和MyCounter子类:
class Counter {
int value;
void inc() {
value++;
}
void incBy(int n) {
value += n;
}
}
class MyCounter extends Counter {
void inc() {
incBy(1);
}
}
Run Code Online (Sandbox Code Playgroud)
实例化和使用MyCounter:
MyCounter m = new MyCounter();
m.inc();
System.out.println(m.value);
m.incBy(2);
System.out.println(m.value);
Run Code Online (Sandbox Code Playgroud)
输出如预期:
1
3
Run Code Online (Sandbox Code Playgroud)
到现在为止还挺好.现在,如果基类,Counter.incBy()将更改为:
void incBy(int n) {
for (; n > 0; n--) {
inc();
}
}
Run Code Online (Sandbox Code Playgroud)
基类Counter仍然完美无瑕且可操作.但是会MyCounter出现故障:MyCounter.inc()调用Counter.incBy(),inc()但是由于动态调度而调用,它会调用MyCounter.inc()...是...无限循环.堆栈溢出错误.
现在让我们看一下相同的例子,这次写在Go:
type Counter struct {
value int
}
func (c *Counter) Inc() {
c.value++
}
func (c *Counter) IncBy(n int) {
c.value += n
}
type MyCounter struct {
Counter
}
func (m *MyCounter) Inc() {
m.IncBy(1)
}
Run Code Online (Sandbox Code Playgroud)
测试它:
m := &MyCounter{}
m.Inc()
fmt.Println(m.value)
m.IncBy(2)
fmt.Println(m.value)
Run Code Online (Sandbox Code Playgroud)
输出与预期一致(在Go Playground上尝试):
1
3
Run Code Online (Sandbox Code Playgroud)
现在让我们改变Counter.Inc() 我们在Java示例中所做的相同方式:
func (c *Counter) IncBy(n int) {
for ; n > 0; n-- {
c.Inc()
}
}
Run Code Online (Sandbox Code Playgroud)
它运行完美,输出相同.在Go Playground尝试一下.
这里发生的是MyCounter.Inc()将调用Counter.IncBy()哪个将调用Inc(),但这Inc()将是Counter.Inc(),所以这里没有无限循环.Counter甚至都不知道MyCounter,它没有任何关于嵌入器MyCounter值的参考.