我目前正在学习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
?
答案 0 :(得分:3)
首先,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的合法选择器,则会调用struct x中嵌入字段的字段或方法f。
提升字段的作用类似于结构的普通字段,除了它们不能用作结构的复合文字中的字段名称。
给定结构类型S和名为T的类型,提升的方法包含在结构的方法集中,如下所示:
如果S包含嵌入字段T,则S和* S的方法集都包括带有接收方T的提升方法。* S的方法集还包括带接收方* T的提升方法。
< / LI>如果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()
}
playground:https://play.golang.org/p/BuBL69LqeY6