接口概念
Go语言提供了接口(interface)这类数据类型,其把所有的具有共性的方法定义在一起,任何其他类型只要实现了这些方法就是实现了这个接口。
Interface可以定义一组方法,而这些方法在interface(){}里是不需要实现的,且其中不能包含任何变量。在Go中的接口我们在外部实现时,只需一个变量含有接口类型中的所有方法,那么这个变量就实现了这个接口。与此同时,如果一个变量同时含有了多个interface类型的方法,那么这个变量就实现了多个接口。
需要我们注意的是,如果一个变量只含有一个interface的部分方法,那么这个变量就没有实现这个接口。
注:接口类似于C++中对象的概念,其是一个规范,不管底层怎么实现。比如车都有轮子、方向盘等,但宝马、比亚迪等不同牌子的车有不同的实现,但都遵循这个规范。
其重要意义是提供一种规范,类似于C++中的父类,但是实现多态无需使用virtual函数
代码实例
下面我们看一个代码实例
//Interface 类型可以定义一组方法,但是这些不需要实现。
//并且interface 不能包含任何变量。
package main
import (
"fmt"
)
type Carer interface {
GetName() string
Run()
DiDi()
}
//--------------------宝马
type BMW struct {
Name string
}
func (p *BMW) GetName() string { //实现三个接口
return p.Name
}
func (p *BMW) Run() {
fmt.Printf("%s is running\n", p.Name)
}
func (p *BMW) DiDi() {
fmt.Printf("%s is didi\n", p.Name)
}
//-------------------比亚迪
type BYD struct {
Name string
}
func (p *BYD) GetName() string { //实现三个接口
return p.Name
}
func (p *BYD) Run() {
fmt.Printf("%s is running\n", p.Name)
}
func (p *BYD) DiDi() {
fmt.Printf("%s is didi\n", p.Name)
}
func main() {
var car Carer
fmt.Println(car)
var bmw BMW
bmw.Name = "BMW"
car = &bmw
car.Run()
var byd BYD
byd.Name = "BYD"
car = &byd
car.Run()
}
如果我们少实现一个方法,我们会看到结果报错:missing DiDi method
代码如下:
package main
import (
"fmt"
)
type Carer interface {
GetName() string
Run()
DiDi()
}
//-------------------比亚迪
type BYD struct {
Name string
}
func (p *BYD) GetName() string { //实现三个接口
return p.Name
}
func (p *BYD) Run() {
fmt.Printf("%s is running\n", p.Name)
}
//不实现DiDi()
func main() {
var byd BYD
byd.Name = "BYD"
car = &byd
car.Run()
}
Go语言包中的实例-自定义结构体调用Sort排序
Go语言中定义了一个Sort函数,其输入是一个接口,详情如下两图所示,这意味我们自定义的结构体,如果实现了这个接口中的方法,就能调用这个Sort进行排序。
代码如下:
package main
import (
"fmt"
"math/rand"
"sort"
)
type Student struct {
Name string
Id string
Age int
}
type StudentArray []Student //取别名
func (p StudentArray) Len() int {
return len(p)
}
func (p StudentArray) Less(i, j int) bool {
return p[i].Name > p[j].Name
}
func (p StudentArray) Swap(i, j int) {
p[i], p[j] = p[j], p[i]
}
func main() {
var stus StudentArray
for i := 0; i < 10; i++ {
stu := Student{
Name: fmt.Sprintf("stu%d", rand.Intn(100)),
Id: fmt.Sprintf("110%d", rand.Intn(10)),
Age: rand.Intn(100),
}
stus = append(stus, stu)
}
for _, v := range stus {
fmt.Println(v)
}
fmt.Println("\n")
sort.Sort(stus)
for _, v := range stus {
fmt.Println(v)
}
}
接口嵌套
代码实例:
package main
import (
"fmt"
)
type Reader interface {
Read()
}
type Writer interface {
Write()
}
type ReadWriter interface {
Reader
Writer
}
func Test(rw ReadWriter) { //传入为接口
rw.Read()
rw.Write()
}
//用File类实现这个接口
type File struct {
}
func (f *File) Read() {
fmt.Println("read data")
}
func (f *File) Write() {
fmt.Println("write data")
}
func main() {
var f *File
var b interface{}
b = f
v, ok := b.(ReadWriter) //判断一个变量是否实现了指定接口
fmt.Println(v, ok)
// Test(&f) //文件类实现了这个接口,就能用调用这个接口
}
类型断言
如下面代码所示,由于Test()传入的是一个接口,其并不知道到它是什么类型,如果我们没有a.(int)这个类型断言,就会报错。
package main
import (
"fmt"
)
func Test(a interface{}) {
b := a.(int)
b += 3
fmt.Println(b)
}
func main() {
var b int
Test(b)
}
对类型的判断代码:传入一个函数判断传入参数的类型
package main
import (
"fmt"
)
type Student struct {
Name string
Sex string
}
func Test(a interface{}) {
b, ok := a.(Student)
if ok == false {
fmt.Println("convert failed")
return
}
// b += 3
fmt.Println(b)
}
func just(items ...interface{}) {
for index, v := range items {
switch v.(type) {
case bool:
fmt.Println("%d params is bool, value is %v\n", index, v)
case int, int64, int32:
fmt.Println("%d params is int, value is %v\n", index, v)
case float32, float64:
fmt.Println("%d params is float, value is %v\n", index, v)
case string:
fmt.Printf("%d params is string, value is %v\n", index, v)
case Student:
fmt.Printf("%d params is student, value is %v\n", index, v)
}
}
}
func main() {
var b Student = Student{
Name: "stu01",
Sex: "female",
}
Test(b)
just(28, 8.2, "this is a test")
}
空接口
interface{},接口中一个方法也没有,所以任何类型都实现了空接口,也就是任何变量都可以赋值给空接口。
接口应用范例:通用链表类
package main
import (
"fmt"
)
type LinkNode struct {
data interface{}
next *LinkNode
}
type Link struct {
head *LinkNode
tail *LinkNode
}
func (p *Link) InsertTail(data interface{}) {
node := &LinkNode{
data: data,
next: nil,
}
if p.tail == nil && p.head == nil {
p.tail = node
p.head = node
return
}
p.tail.next = node
p.tail = node
}
func (p *Link) Trans() {
g := p.head
for g != nil {
fmt.Println(g.data)
g = g.next
}
}
func main() {
var intLink Link
for i := 0; i < 10; i++ {
intLink.InsertTail(i)
}
intLink.Trans()
}