内容简介:1.接口定义Go 语言的接口类型非常特别,它的作用和 Java 语言的接口一样,但是在形式上有很大的差别。Java 语言需要在类的定义上显式实现了某些接口,才可以说这个类具备了接口定义的能力。但是 Go 语言的接口是隐式的,只要结构体上定义的方法在形式上(名称、参数和返回值)和接口定义的一样,那么这个结构体就自动实现了这个接口,我们就可以使用这个接口变量来指向这个结构体对象。下面我们看个例子
一、概念
1.接口定义
Go 语言的接口类型非常特别,它的作用和 Java 语言的接口一样,但是在形式上有很大的差别。Java 语言需要在类的定义上显式实现了某些接口,才可以说这个类具备了接口定义的能力。但是 Go 语言的接口是隐式的,只要结构体上定义的方法在形式上(名称、参数和返回值)和接口定义的一样,那么这个结构体就自动实现了这个接口,我们就可以使用这个接口变量来指向这个结构体对象。下面我们看个例子
package main import "fmt" // 可以闻 type Smellable interface { smell() } // 可以吃 type Eatable interface { eat() } // 苹果既可能闻又能吃 type Apple struct {} func (a Apple) smell() { fmt.Println("apple can smell") } func (a Apple) eat() { fmt.Println("apple can eat") } // 花只可以闻 type Flower struct {} func (f Flower) smell() { fmt.Println("flower can smell") } func main() { var s1 Smellable var s2 Eatable var apple = Apple{} var flower = Flower{} s1 = apple s1.smell() s1 = flower s1.smell() s2 = apple s2.eat() } -------------------- apple can smell flower can smell apple can eat
上面的代码定义了两种接口,Apple 结构体同时实现了这两个接口,而 Flower 结构体只实现了 Smellable 接口。我们并没有使用类似于 Java 语言的 implements 关键字,结构体和接口就自动产生了关联。
2.空接口
如果一个接口里面没有定义任何方法,那么它就是空接口,任意结构体都隐式地实现了空接口。
Go 语言为了避免用户重复定义很多空接口,它自己内置了一个,这个空接口的名字特别奇怪,叫 interface{} ,初学者会非常不习惯。之所以这个类型名带上了大括号,那是在告诉用户括号里什么也没有。我始终认为这种名字很古怪,它让代码看起来有点丑陋。
空接口里面没有方法,所以它也不具有任何能力,其作用相当于 Java 的 Object 类型,可以容纳任意对象,它是一个万能容器。比如一个字典的 key 是字符串,但是希望 value 可以容纳任意类型的对象,类似于 Java 语言的 Map 类型,这时候就可以使用空接口类型 interface{}。
package main import "fmt" func main() { // 连续两个大括号,是不是看起来很别扭 var user = map[string]interface{}{ "age": 30, "address": "Beijing Tongzhou", "married": true, } fmt.Println(user) // 类型转换语法来了 var age = user["age"].(int) var address = user["address"].(string) var married = user["married"].(bool) fmt.Println(age, address, married) } ------------- map[age:30 address:Beijing Tongzhou married:true] 30 Beijing Tongzhou true
代码中 user 字典变量的类型是 map[string]interface{},从这个字典中直接读取得到的 value 类型是 interface{},需要通过类型转换才能得到期望的变量。
3.用接口来模拟多态
package main import "fmt" type Fruitable interface { eat() } type Fruit struct { Name string // 属性变量 Fruitable // 匿名内嵌接口变量 } func (f Fruit) want() { fmt.Printf("I like ") f.eat() // 外结构体会自动继承匿名内嵌变量的方法 } type Apple struct {} func (a Apple) eat() { fmt.Println("eating apple") } type Banana struct {} func (b Banana) eat() { fmt.Println("eating banana") } func main() { var f1 = Fruit{"Apple", Apple{}} var f2 = Fruit{"Banana", Banana{}} f1.want() f2.want() } --------- I like eating apple I like eating banana
使用这种方式模拟多态本质上是通过组合属性变量(Name)和接口变量(Fruitable)来做到的,属性变量是对象的数据,而接口变量是对象的功能,将它们组合到一块就形成了一个完整的多态性的结构体。
4.接口的组合继承
接口的定义也支持组合继承,比如我们可以将两个接口定义合并为一个接口如下
type Smellable interface { smell() } type Eatable interface { eat() } type Fruitable interface { Smellable Eatable }
这时 Fruitable 接口就自动包含了 smell() 和 eat() 两个方法,它和下面的定义是等价的。
type Fruitable interface { smell() eat() }
5.接口变量的赋值
变量赋值本质上是一次内存浅拷贝,切片的赋值是拷贝了切片头,字符串的赋值是拷贝了字符串的头部,而数组的赋值呢是直接拷贝整个数组。接口变量的赋值会不会不一样呢?接下来我们做一个实验
package main import "fmt" type Rect struct { Width int Height int } func main() { var a interface {} var r = Rect{50, 50} a = r var rx = a.(Rect) r.Width = 100 r.Height = 100 fmt.Println(rx) } ------ {50 50}
二、使用
1.如果我们发现我们需要以同样的方式处理Audio和Video,我们可以定义一个Streamer接口来代表它们之间相同的部分而不必对已经存在的类型做改变。
每一个具体类型的组基于它们相同的行为可以表示成一个接口类型。不像基于类的语言,他们一个类实现的接口集合需要进行显式的定义,在Go语言中我们可以在需要的时候定义一个新的抽象或者特定特点的组,而不需要修改具体类型的定义。当具体的类型来自不同的作者时这种方式会特别有用。当然也确实没有必要在具体的类型中指出这些共性。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- golang笔记之接口(二)
- Golang学习笔记 方法和接口
- Golang学习笔记之接口(interface)
- Golang 学习笔记三 接口、错误异常
- Golang 学习笔记四 结构体 接口
- b站代码阅读笔记 业务接口篇
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
垃圾回收算法手册:自动内存管理的艺术
Richard Jones、Eliot Moss、Antony Hosking / 王雅光、薛迪 / 机械工业出版社 / 2016-3 / 139
在自动内存管理领域,Richard Jones于1996年出版的《Garbage Collection:Algorithms for Automatic Dynamic Memory Management》可谓是一部里程碑式的作品。接近20年过去了,垃圾回收技术得到了非常大的发展,因此有必要将该领域当前最先进的技术呈现给读者。本书汇集了自动内存管理研究者和开发者们在过去50年间的丰富经验,在本书中......一起来看看 《垃圾回收算法手册:自动内存管理的艺术》 这本书的介绍吧!