【问题标题】:Interface inheritance in non-Struct types非结构类型中的接口继承
【发布时间】:2018-07-06 22:18:49
【问题描述】:

我目前正在学习 Go 中的接口,但我被这段代码卡住了:

package main

import (
    "fmt"
    "math"
)

// CommonMath is a common interface for math types
type CommonMath interface {
    Abs() float64
}

// Float64 is a custom float64 type
type Float64 float64

// Abs returns the modulus of objects implementing CommonMath
func (f Float64) Abs() float64 {
    if f < 0 {
        return -float64(f)
    }
    return float64(f)
}

// AbsSquared returns the square of objects implementing CommonMath
func AbsSquared(num CommonMath) float64 {
    return num.Abs() * num.Abs()
}

func main() {
    var f Float64
    f = -5

    type newF Float64
    var newFloat newF
    newFloat = 10.5

    fmt.Println(f)
    fmt.Println(f.Abs())
    fmt.Println(AbsSquared(newFloat))
}

事实证明,它无法编译,因为newFloat 没有实现接口CommonMath。属于newF类型,是实现接口的自定义Float64类型,不知道怎么回事。为了让事情变得更奇怪,我将 newF 和 newFloat 的声明替换为以下内容,它们实现相同但作为结构:

type newF struct {
    Float64
}
newFloat := newF{10.5}

突然之间,代码构建得非常好。这是否意味着只有结构体才能实现父类型的接口,因此不允许将类型newF直接声明为Float64

【问题讨论】:

  • 首先,Go 中没有继承,这可能是混淆的一部分。声明newF 的唯一原因是删除方法。如果您不想为newF 创建新方法集,请不要使用新类型。
  • 也许继承不是这个词,但派生结构确实实现了其父实现的接口。为什么其他类型不会发生这种情况?
  • 没有任何东西可以“继承”方法。您的结构有一个嵌入的Float64 字段,并且这些方法会自动委托给Float64 实现。请参阅 Effective Go 中的 embeddinglanguage spec

标签: inheritance go interface


【解决方案1】:

首先,在 Go 中根本没有继承。网上有几篇文章讨论 Go 放弃 oop 的许多功能的设计决策,但这确实超出了这个答案。

看你的情况。很明显Float64 实现了CommonMath。它具有CommonMath 所需的方法集,因此适合该界面。

但是,当您声明另一种类型type newF Float64 时,新类型不会复制方法集,只会复制数据结构。这是在spec 中指定的:

类型定义创建一个新的、不同的类型,它具有与给定类型相同的基础类型和操作,并将标识符绑定到它。

新类型称为已定义类型。它不同于任何其他类型,包括创建它的类型。

已定义的类型可能具有与之关联的方法。它不继承绑定到给定类型的任何方法,但接口类型或复合类型元素的方法集保持不变:

// A Mutex is a data type with two methods, Lock and Unlock.
type Mutex struct         { /* Mutex fields */ }
func (m *Mutex) Lock()    { /* Lock implementation */ }
func (m *Mutex) Unlock()  { /* Unlock implementation */ }

// NewMutex has the same composition as Mutex but its method set is empty.
type NewMutex Mutex
// The method set of the base type of PtrMutex remains unchanged,
// but the method set of PtrMutex is empty.
type PtrMutex *Mutex

// The method set of *PrintableMutex contains the methods
// Lock and Unlock bound to its embedded field Mutex.
type PrintableMutex struct {
    Mutex
}

// MyBlock is an interface type that has the same method set as Block.
type MyBlock Block

接下来来type NewF struct { Float64 }。乍一看,这看起来很像继承,但在 Go 中再一次没有这样的东西。它称为嵌入组合。同样,spec

使用类型声明但没有显式字段名称的字段称为嵌入字段。嵌入字段必须指定为类型名称 T 或指向非接口类型名称 *T 的指针,并且 T 本身可能不是指针类型。非限定类型名称充当字段名称。

如果 x.f 是表示该字段或方法 f 的合法选择器,则称为提升结构 x 中嵌入字段的字段或方法 f。

提升字段的作用类似于结构的普通字段,但它们不能用作结构的复合文字中的字段名称。

给定一个结构类型 S 和一个名为 T 的类型,提升的方法包含在结构的方法集中,如下所示:

  • 如果 S 包含嵌入字段 T,则 S 和 *S 的方法集都包含带有接收者 T 的提升方法。*S 的方法集还包括带有接收者 *T 的提升方法。

  • 如果 S 包含嵌入字段 *T,则 S 和 *S 的方法集都包含带有接收者 T 或 *T 的提升方法。

所以,这里的诀窍是促销Float64 的方法被提升为NewF。同样,它在某些方面可能看起来像继承,但它是不同的。请注意,提升的方法仍然属于嵌入式类型,接收者将永远是原始的。以下代码打印 4.

package main

import (
    "fmt"
)

type A struct {}

type B struct {A}

func (A) P() int {
    return 4
}

func (B) P() int {
    return 5
}

func (a A) S() {
    fmt.Println(a.P())
}

func main() {
    B{}.S()
}

游乐场:https://play.golang.org/p/BuBL69LqeY6

【讨论】:

  • 谢谢!这解释了很多。希望有一种方法可以在非结构类型中使用嵌入。
猜你喜欢
  • 1970-01-01
  • 2011-03-25
  • 2020-03-02
  • 1970-01-01
  • 2022-01-04
  • 2013-02-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多