什么是接口
Go接口是一种数据类型,它把所有的具有共性的方法定义在一起,任何其他类型只要实现了这些方法就是实现了这个接口。
在 go 语言中,接口有下面几个特点:
- 可以包含0个或多个方法的签名
- 只定义方法的签名,不包含实现
- 实现接口不需要显式的声明,只需实现相应方法即可
接口的定义
利用关键字 interface 来定义一个接口,接口是一组方法的集合。
接口定义格式:
type 接口名称 interface{
方法名称1(可能会用到的参数,可不传) 返回类型
方法名称2(可能会用到的参数,可不传) 返回类型
...
}
复制代码
例如:
type People interface {
Show(name string, age int) (id int, err error)
Set(name string, age int)
}
复制代码
接口的实现
在 go 语言中,接口的实现是隐式的,不需要显示声明实现了接口,当一个类型为接口中的所有方法提供定义时,它被称为实现该接口。
接口的实现示例如下:
package main
import (
"fmt"
)
type USB interface {
Name() string
Connect()
}
type PhoncConnecter struct {
name string
}
// 因为以下方法 Name()、Connect() 与接口USB的方法相同,因此结构体PhoncConnecter实现了USB接口
func (pc PhoncConnecter) Name() string {
return pc.name
}
func (pc PhoncConnecter) Connect() {
fmt.Println(pc.name)
}
func main() {
// 第一种直接在声明结构时赋值
var a USB // 声明一个接口类型的变量
a = PhoncConnecter{"PhoneC"} // 因为 PhoncConnecter 实现了接口 USB,所以可以将该类型的实例赋给接口的变量
a.Connect()
// 第二种,先给结构赋值后在将值给接口去调用
var b = PhoncConnecter{}
b.name = "b"
var c USB // 声明一个接口类型的变量
c = b
c.Connect()
}
复制代码
输出为:
PhoneC
b
复制代码
以上示例定义了USB的接口,并声明了两个方法 Name()、Connect()
,接着定义了PhoncConnecter
结构并声明了一个name
的变量,通过方法特性,对结构也同样声明了两个方法Name()、Connect()
。在GO中,只要实现了接口中定义的方法,默认就代表类似于其它语言中的继承,继承了那个接口,所以在main函数中,就可以通过声明接口和结构进行相对应的操作,从而达到代码重复使用。
接口的值类型
1. 接口的内部表现
一个接口可以被认为是由一个元组(类型,值)在内部表示的。type是接口的基础具体类型,value是具体类型的值。
接口是一系列接口的集合,是一种抽象数据类型,接口变量可以引用任何实现了接口的全部方法的具体数据类型的值。
接口变量存储了两部分信息,一个是分配给接口变量的具体值(接口实现者的值),一个是值的类型的描述器(接口实现者的类型),形式是(value, concrete type),而不是(value, interface type)。
package main
import (
"fmt"
)
type Test interface {
Tester()
}
type MyFloat float64
func (m MyFloat) Tester() {
fmt.Println(m)
}
func describe(t Test) {
fmt.Printf("Interface 类型: %T , 值: %v\n", t, t)
}
func main() {
var t Test
f := MyFloat(89.7)
t = f
describe(t)
t.Tester()
}
复制代码
运行结果:
Interface 类型: main.MyFloat , 值: 89.7
89.7
复制代码
2. 空接口
空接口就是不包含任何方法的接口,它表示为 interface {}
。正因为如此,所有的类型都实现了空接口。
虽然空接口起不到任何作用,但是空接口在需要存储任何类型数值的时候非常有用,因为空接口可以存储任意类型的数据。
package main
import (
"fmt"
)
func describe(i interface{}) {
fmt.Printf("Type = %T, value = %v\n", i, i)
}
func main() {
// 任何类型的变量传入都可以
s := "Hello World"
i := 55
strt := struct {
name string
}{
name: "Naveen R",
}
describe(s)
describe(i)
describe(strt)
}
复制代码
运行结果:
Type = string, value = Hello World
Type = int, value = 55
Type = struct { name string }, value = {Naveen R}
复制代码
3. 类型断言
interface{}
可用于向函数传递任意类型的变量,但对于函数内部,该变量仍然为 interface{}
类型(空接口类型),而不是传入的实参类型。
接口类型向普通类型的转换称为类型断言(运行期确定),类型断言用于提取接口的基础值,语法:i.(T)
func printArray(arr interface{}){
// arr 是空接口,不是数组类型,报错
for _, v: = range arr{
fmt.Print(v," ")
}
  fmt.Println()
}
复制代码
可以通过类型断言将接口类型转换为切片类型
func printArray(arr interface{}){
// 通过断言实现类型转换
a,_ := arr.([]int)
for _,v:=range a{
fmt.Println(v, " ")
}
fmt.Println()
}
复制代码
在使用类型断言时,最好判断断言是否成功
b,ok := a.(T)
if ok {
...
}
复制代码
4. 类型判断
类型判断的语法类似于类型断言,在类型断言的语法i.(type)
中,类型 type 应该由类型转换的关键字 type
替换。 类型断言可以配合switch语句进行判断:
package main
import (
"fmt"
)
func findType(i interface{}) {
switch i.(type) {
case string:
fmt.Printf("String: %s\n", i.(string))
case int:
fmt.Printf("Int: %d\n", i.(int))
default:
fmt.Printf("Unknown type\n")
}
}
func main() {
findType("Naveen")
findType(77)
findType(89.98)
}
复制代码
运行结果:
String: Naveen
Int: 77
Unknown type
复制代码
接口的使用
1. 内嵌接口
一个接口可以包含一个或多个其他的接口,这相当于直接将这些内嵌接口的方法列举在外层接口中一样。
比如接口 File 包含了 ReadWrite 和 Lock 的所有方法,它还额外有一个 Close() 方法。
type ReadWrite interface {
Read(b Buffer) bool
Write(b Buffer) bool
}
type Lock interface {
Lock()
Unlock()
}
type File interface {
ReadWrite
Lock
Close()
}
复制代码
2. 接口变量赋值
可以将一个实现接口的对象实例赋值给接口,也可以将另外一个接口赋值给接口。
-
通过对象实例赋值
如果一个类型实现了某个接口,可以将类型的实例赋值给接口变量 -
通过接口赋值
接口A和接口B,如果接口A是接口B的子集,B类型的变量赋给A类型的变量
package main
import (
"bytes"
"io"
"os"
// "time"
)
func main() {
// File、bytes.Buffer 实现了Writer
var w io.Writer // A
w = os.Stdout
w = new (bytes.Buffer)
// w = time.Second // error, 因为time.Second没有实现了Writer
var rwc io.ReadWriteCloser // B
rwc = os.Stdout // File 实现了 io.ReadWriteCloser 接口
w = rwc
}
复制代码
3. 用接口实现多态
go 语言多态是将实现接口的不同类型的实例赋给接口变量,让接口的方法拥有不同的行为。
package main
import "fmt"
type MyGreeting interface {
Greet(name string) string
}
type EnglishGreeting struct {
}
type ChinesGreeting struct {
}
func (this *EnglishGreeting) Greet(name string) string {
return "Hello " + name
}
func (this *ChinesGreeting) Greet(name string) string {
return "你好 " + name
}
func main() {
var greet MyGreeting = new(EnglishGreeting)
fmt.Println(greet.Greet("Bill"))
greet = new(ChinesGreeting)
fmt.Println(greet.Greet("Bill"))
}
复制代码
运行结果:
Hello Bill
你好 Bill
复制代码
转载于:https://juejin.im/post/5cf8d12851882556174ddd0d