您的位置:首页 > 编程语言 > Go语言

Golang学习笔记1-组合和接口

2018-03-01 21:42 260 查看

主要内容

1.组合
2.接口
3.常见问题
4.总结


1.组合

Golang中没有OOP中继承的概念,取而代之的是组合,同样起到属性和方法继承 的作用,特别是匿名组合,其表现形式和继承的行为类似,但是却有很多不同的地方,相对于继承、多态这些典型的OOP思想,组合显得更加简洁灵活。

1.1.一般组合

type Wifi struct {
Name string
}

type Phone struct {
WifiMdl Wifi
}

func Test_Group(t *testing.T) {
phone := Phone{}
phone.WifiMdl = Wifi{
Name: "..."
}
...
}


这是一个简单的示例,直接将其他的类型作为自己的内部成员;那么新类型的实例就可以直接对此成员进行赋值和初始化;

1.2.匿名组合

当我们嵌入一个类型,这个类型的方法就变成了外部类型的方法,但是当它被调用时,方法的接受者是内部类型(嵌入类型),而非外部类型。— Effective Go

理解了这句话也就理解匿名组合:简单地可以理解为,内部类型的成员名可理解为内部类型名,在外部类型未定义相同名称的属性和方法时,内部方法可提升为外部类型的属性和方法,但实际上,属性和方法的所有者和调用的接受仍然为内部类型

// 内部类型
type People struct {
Name string
}

func (p *People) GetName() {
log.Print(p.Name)
}

// 外部类型
type Man struct {
People
}

func Test_Group(t *testing.T) {
man := Man{}
man.Name = "villare"
man.GetName()
}


匿名组合,即嵌入的类型没有自己的属性名,这种方式的组合和一般的组合最大的不同在于,外部类型的实例可以直接通过自身访问内部类型的属性和方法,“看上去外部类型作为属性和方法的所有者”,形式上与继承的类似;

特别的,如果内部类型也为指针

// 内部类型
type People struct {
Name string
}

func (p *People) GetName() {
log.Print(p.Name)
}

// 外部类型
type Man struct {
*People
}

func Test_Group(t *testing.T) {
man := Man{}
//以下代码报错(invalid memory address or nil pointer dereference...)
//先初始化 man := Man{&People{},}
man.Name = "villare"
man.GetName()
}


指针类型的匿名组合在直接调用内部类型的方法时,会出现地址未分配的错误;可见,指针类型的匿名组合需要在外部类型初始化的同时进行手动初始化,不会自动初始化内部类型;

由此可见,匿名组合中内部方法的接受者实际上还是内部类型;

2.接口

2.1.接口的定义

type People interface {
Sex()
}


接口的定义和其他语言有相似之处,直接定义接口方法,不过不支持静态成员和默认方法的定义形式;

由于golang接口的实现形式使得它没必要支持这些特性

2.2.接口的实现

type People interface {
Sex()
}

type Man struct {}

func (m *Man) Sex() {
log.Print("Man")
}

type Woman struct {}

func (m *Woman) Sex() {
log.Print("Woman")
}

func Test_Inter(t *testing.T) {
var t1 People

t1 = new(Man)
t1.Sex()

t1 = new(Woman)
t1.Sex()
}



golang的接口实现遵循一个原则:如果一个类型实现了接口定义的所有方法,那么就认为此类型实现了这个接口

golang中方法不能重名,不支持重载

golang不使用接口指针,直接使用接口类型,注意和java、c++的区别

3.常见问题

3.1.组合中的访问控制

golang默认的访问控制规则是:首字母大写(Public),首字母小写(private)

对于组合来说,内部类型的私有成员在外部类型中可见,但是仍然为私有成员(不可通过实例直接访问)

3.2.嵌入冲突

type Time struct {
Name string
}

// 正确示例
type UTC struct {
time.Time
T2 Time
}

// 错误示例
type UTC struct {
time.Time
Time
}


在嵌入多个类型时,保证成员名不同,匿名组合的成员名为类型名称

即使不同包路径不同,只要类型名相同,匿名组合均有冲突

3.2.匿名组合的冲突

注意一点,成员和方法的优先级:

外部类型 > 内部类型 > 内部类型的内部类型 > …

如果出现歧义(Ambiguous),通过加类型名的形式调用,明确一点“属性和方法的所有者和调用者均属于内部方法”

关注下面一个实例

type People struct {
Name string
}

func (p *People) GetSex() {
log.Print("Man Or Woman")
}

type Man struct {
People
}

func (p *Man) GetSex() {
log.Print("Man")
}

type Child struct {
Man
}

func Test_Group(t *testing.T) {
cld := Child{}
cld.GetSex()
cld.Man.GetSex()
cld.People.GetSex()
}

//输出为
Man
Man
Man Or Woman


4.总结

总的来说,golang组合和接口的两个概念还是相对比较好理解的,规则也更加契合主观认知,减少了理解和学习的成本;但在简洁的同时也并没有减弱golang语言本身的表达和设计能力;
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: