Go语言中的继承

huangapple go评论86阅读模式
英文:

Inheritance in Go

问题

为什么Go语言没有类型继承?

(类型继承是指当定义一个对象的类时,任何定义的子类都可以继承一个或多个通用类的定义的概念)

英文:

Why doesn't Go have Type Inheritance?

(The concept that when a class of an objects is defined, any subclass that is defined can inherit the definitions of one or more general classes)

答案1

得分: 24

这是由语言创建者在F.A.Q.中回答的:

“面向对象编程,至少在最知名的语言中,涉及太多关于类型之间关系的讨论,而这些关系通常可以自动推导出来。Go采用了一种不同的方法。

与其要求程序员事先声明两个类型之间的关系,Go中的类型会自动满足任何指定了其方法子集的接口。除了减少繁琐的记录外,这种方法还具有真正的优势。类型可以同时满足多个接口,而不涉及传统多重继承的复杂性。接口可以非常轻量级——一个甚至零个方法的接口可以表达一个有用的概念。接口可以在后期添加,如果出现新的想法或进行测试,而无需注释原始类型。由于类型和接口之间没有明确的关系,因此无需管理或讨论类型层次结构。”

另请参阅:组合优于继承原则

英文:

This was answered by the language creators in the F.A.Q.:

>Object-oriented programming, at least in the best-known languages, involves too much discussion of the relationships between types, relationships that often could be derived automatically. Go takes a different approach.
>
>Rather than requiring the programmer to declare ahead of time that two types are related, in Go a type automatically satisfies any interface that specifies a subset of its methods. Besides reducing the bookkeeping, this approach has real advantages. Types can satisfy many interfaces at once, without the complexities of traditional multiple inheritance. Interfaces can be very lightweight—an interface with one or even zero methods can express a useful concept. Interfaces can be added after the fact if a new idea comes along or for testing—without annotating the original types. Because there are no explicit relationships between types and interfaces, there is no type hierarchy to manage or discuss.

See also: Composition over inheritance principle.

答案2

得分: 7

如果您需要继承以实现可重用性,这个示例展示了如何重用形状接口的宽度/高度。

package main

// 与 C++ 示例进行比较:http://www.tutorialspoint.com/cplusplus/cpp_interfaces.htm

import (
    "fmt"
)

// 接口
type Shape interface {
    Area() float64
    GetWidth() float64
    GetHeight() float64
    SetWidth(float64)
    SetHeight(float64)
}

// 可重用部分,只实现接口的 SetWidth 和 SetHeight 方法
// {

type WidthHeight struct {
    width  float64
    height float64
}

func (this *WidthHeight) SetWidth(w float64) {
    this.width = w
}
func (this *WidthHeight) SetHeight(h float64) {
    this.height = h
}
func (this *WidthHeight) GetWidth() float64 {
    return this.width
}
func (this *WidthHeight) GetHeight() float64 {
    fmt.Println("在 WidthHeight.GetHeight 中")
    return this.height
}

// }

type Rectangle struct {
    WidthHeight
}

func (this *Rectangle) Area() float64 {
    return this.GetWidth() * this.GetHeight() / 2
}

// 重写
func (this *Rectangle) GetHeight() float64 {
    fmt.Println("在 Rectangle.GetHeight 中")
    // 如果您仍然需要 WidthHeight 的 GetHeight 方法
    return this.WidthHeight.GetHeight()
}

func main() {
    var r Rectangle
    var i Shape = &r
    i.SetWidth(4)
    i.SetHeight(6)

    fmt.Println(i)
    fmt.Println("宽度:", i.GetWidth())
    fmt.Println("高度:", i.GetHeight())
    fmt.Println("面积:", i.Area())

}

结果:

&{{4 6}}
宽度: 4
在 Rectangle.GetHeight 中
在 WidthHeight.GetHeight 中
高度: 6
在 Rectangle.GetHeight 中
在 WidthHeight.GetHeight 中
面积: 12
英文:

If you need inheritance for reusable, this example shows how I reuse the width/height of a shape interface,

package main
// compare to a c++ example: http://www.tutorialspoint.com/cplusplus/cpp_interfaces.htm
import (
"fmt"
)
// interface
type Shape interface {
Area() float64
GetWidth() float64
GetHeight() float64
SetWidth(float64)
SetHeight(float64)
}
// reusable part, only implement SetWidth and SetHeight method of the interface
// {
type WidthHeight struct {
width  float64
height float64
}
func (this *WidthHeight) SetWidth(w float64) {
this.width = w
}
func (this *WidthHeight) SetHeight(h float64) {
this.height = h
}
func (this *WidthHeight) GetWidth() float64 {
return this.width
}
func (this *WidthHeight) GetHeight() float64 {
fmt.Println("in WidthHeight.GetHeight")
return this.height
}
// }
type Rectangle struct {
WidthHeight
}
func (this *Rectangle) Area() float64 {
return this.GetWidth() * this.GetHeight() / 2
}
// override
func (this *Rectangle) GetHeight() float64 {
fmt.Println("in Rectangle.GetHeight")
// in case you still needs the WidthHeight's GetHeight method
return this.WidthHeight.GetHeight()
}
func main() {
var r Rectangle
var i Shape = &r
i.SetWidth(4)
i.SetHeight(6)
fmt.Println(i)
fmt.Println("width: ",i.GetWidth())
fmt.Println("height: ",i.GetHeight())
fmt.Println("area: ",i.Area())
}

Result:

&{{4 6}}
width:  4
in Rectangle.GetHeight
in WidthHeight.GetHeight
height:  6
in Rectangle.GetHeight
in WidthHeight.GetHeight
area:  12

答案3

得分: 0

只是想在这里补充一些想法。这是我的观点,因此是主观而不是客观的观点。简单的答案是,在一个专为易于理解和维护的服务器基础架构而设计的语言中,继承(实现继承)的脆弱性和复杂性是不值得的(如果代码易于理解,你可以更好地进行维护)。在Go中,你肯定可以实现多态性,因为它具有接口,类型可以实现接口(通过指定接口方法的子集)。但是,为了代码重用而创建深层继承层次结构会导致脆弱且常常混乱的代码,这在软件变得更大时(任何成功/流行项目最终都会发生)特别难以维护和理解。这就是为什么你会注意到一些现代语言要么避免继承(如Go、Rust等),要么默认禁用继承(如Kotlin等)。继承使用被认为是好的少数几个领域之一是GUI编程,但即使在这方面也在发生变化。例如,大多数现代GUI框架(React、Flutter)更喜欢组合而不是继承。即使Android也正在通过jetpack-compose转向组合风格。

你可以阅读Rob Pike(Go的共同创始人)的这次采访,了解他为什么不选择继承。
https://evrone.com/rob-pike-interview

其他链接:
https://en.wikipedia.org/wiki/Inheritance_(object-oriented_programming)#Issues_and_alternatives

https://reactjs.org/docs/composition-vs-inheritance.html

https://developer.android.com/jetpack/compose/tutorial

https://docs.flutter.dev/resources/architectural-overview#composition

英文:

Just thought to add a few thoughts here. It is my opinion and hence a subjective and not an objective view. The simple answer is the fragility and complexity of inheritance(implementation inheritance) is not worth it in a language that is specially designed for server infrastructures that are easy to understand and maintain (you can maintain better if code is easy to understand). You can definitely achieve polymorphism in Go as it has interfaces and types can implement interfaces (by specifying a subset of interface methods). But creating deep inheritance hierarchies for code re-use leads to fragile and often spaghetti code that is difficult to maintain and reason about especially when the software gets bigger (which happens eventually to any successful/popular project). That's why you will notice that some of the modern languages have either avoided inheritance (Go, Rust etc) or disabled (Kotlin etc) it by default. One of the few places where inheritance use is considered good is GUI programming but even that is changing now. For example most of the modern GUI frameworks (React, Flutter) prefer composition over inheritance. Even Android is moving to composition style via jetpack-compose.

You can read this interview of Rob pike(co-creator of Go) why he didn't go for inheritance.
https://evrone.com/rob-pike-interview

Other links:
https://en.wikipedia.org/wiki/Inheritance_(object-oriented_programming)#Issues_and_alternatives

https://reactjs.org/docs/composition-vs-inheritance.html

https://developer.android.com/jetpack/compose/tutorial

https://docs.flutter.dev/resources/architectural-overview#composition

答案4

得分: -1

我认为嵌入式结构体的定义已经足够接近了:

package main
import "net/url"
type address struct { *url.URL }
func newAddress(rawurl string) (address, error) {
p, e := url.Parse(rawurl)
if e != nil {
return address{}, e
}
return address{p}, nil
}
func main() {
a, e := newAddress("https://stackoverflow.com")
if e != nil {
panic(e)
}
{ // 继承
s := a.String()
println(s)
}
{ // 完全限定
s := a.URL.String()
println(s)
}
}

https://golang.org/ref/spec#Struct_types

英文:

I think embedding fits the definition close enough:

package main
import "net/url"
type address struct { *url.URL }
func newAddress(rawurl string) (address, error) {
p, e := url.Parse(rawurl)
if e != nil {
return address{}, e
}
return address{p}, nil
}
func main() {
a, e := newAddress("https://stackoverflow.com")
if e != nil {
panic(e)
}
{ // inherit
s := a.String()
println(s)
}
{ // fully qualified
s := a.URL.String()
println(s)
}
}

<https://golang.org/ref/spec#Struct_types>

huangapple
  • 本文由 发表于 2015年12月8日 17:14:18
  • 转载请务必保留本文链接:https://go.coder-hub.com/34151961.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定