Golang interface(接口)
是一种对其他类型行为进行抽象的自定义类型。接口定义使用interface
标识符,用来声明一系列函数签名(函数名、参数、参数类型、函数返回值)。
介绍
接口定义:
type 接口名 interface {
函数1(参数 类型, ...) 返回值类型1
函数2(参数 []类型, ...) 返回值类型2
}
说明:
- 接口用例实现多态
- 接口名:符合变量命名规则
- 函数即接口的行为
- 声明接口变量只需要定义变量类型为接口名,此时变量的值为
nil
- 当一个自定义类型实现了接口类型中声明的所有函数时,则该自定义类型可以赋值给接口变量,并使用接口变量调用实现的接口
- 当两个接口声明同样的函数签名时,则这两个接口完全等价
- 当接口A的方式是接口B方法的,接口A变量可以赋值给接口B变量,但只能调用B的方法
示例
package main
import "fmt"
type Feature interface {
Fly(name string) bool
Swim(name string) bool
}
type Duck struct {
}
// Duck 的相关方法声明为值接受者方法
func (d Duck) Fly(name string) bool {
fmt.Println(name, "can fly")
return true
}
func (d Duck) Swim(name string) bool {
fmt.Println(name, "can swim")
return true
}
type Swan struct {
}
// Swam 的相关方法声明为指针接收者方法
func (s *Swan) Fly(name string) bool {
fmt.Println(name, "can fly")
return true
}
func (s *Swan) Swim(name string) bool {
fmt.Println(name, "can swim")
return true
}
type Goose struct {
}
// Fly 该方法声明为值接受者,Go编译器默认声明指针类型的 Fly 方法
func (g Goose) Fly(name string) bool {
fmt.Println(name, "can fly")
return true
}
// Swim 该方法声明为指针接受者
func (g *Goose) Swim(name string) bool {
fmt.Println(name, "can swim")
return true
}
func doFly(name string, f Feature) {
f.Fly(name)
}
type JustFly interface {
Fly(name string) bool
}
func main() {
var f Feature
fmt.Printf("%T %#v\n", f, f) // <nil> <nil>
// 实现多态
f = Duck{}
fmt.Printf("%T %#v\n", f, f) // main.Duck main.Duck{}
f.Fly("tanghaoya")
f.Swim("tanghaoya")
f = &Swan{} // 只实现指针接收者方法,只能使用指针
fmt.Printf("%T %#v\n", f, f) // main.Swan main.Swan{}
f.Fly("tiane")
f.Swim("tiane")
// 值接收者方法自动添加指针接收者方法,因此可以使用指针赋值
f = &Duck{}
f.Fly("zhouheiya")
f.Swim("zhouheiya")
// 接口需要实现全部方法,否则不能称为接口
//f = Goose{} // Cannot use 'Goose{}' (type Goose) as the type Feature Type does not implement 'Feature' as the 'Swim' method has a pointer receiver
f = &Goose{}
f.Fly("dabaie")
f.Swim("dabaie")
doFly("zhouhaiya", f)
// 接口赋值给接口
var js JustFly = f
// 需要满足对应接口声明的方法和结构体的属性
js.Fly("chicken")
//js.Swim("chicken") 没有该方法
// 接口断言
goose := js.(*Goose) // 转为指针类型
fmt.Printf("%T %#v\n", goose, goose) //*main.Goose &main.Goose{}
goose, ok := js.(*Goose) // 转为指针类型
fmt.Printf("%T %#v %v\n", goose, goose, ok) //*main.Goose &main.Goose{} true
swan, ok := js.(*Swan)
// 类型不匹配,转化失败
fmt.Printf("%T %#v\n", swan, ok) // *main.Swan false
f = Duck{}
js = f
duck := js.(Duck) // 转为值类型
fmt.Printf("%T %#v\n", duck, duck) // main.Duck main.Duck{}
duck, ok = js.(Duck) // 带是否转化成功标识
fmt.Printf("%T %#v %#v\n", duck, duck, ok) // main.Duck main.Duck{} true
// 类型查询,js.(type) 只能使用在 switch 中
//switch js.(type) {
switch v := js.(type) {
case Duck:
fmt.Println("duck")
fmt.Printf("%T\n", v)
//case Swan: // Impossible type switch case: 'Swan' does not implement 'JustFly'
// fmt.Println("swan")
case *Swan: // Impossible type switch case: 'Swan' does not implement 'JustFly'
fmt.Println("*swan")
case Goose:
fmt.Println("goose")
default:
fmt.Println("unknown")
}
}
下面示例是同一个接口:
// A 和 B 是同一个接口,只是名字不同
type A interface {
do()
}
type B interface {
do()
}
断言
断言用来判断一个接口是否可以转化为具体的类型
接口变量.(type)
[类型变量1|*类型变量1] := 接口变量.([类型|*类型])
[类型变量1|*类型变量1], [true|false] := 接口变量.([类型|*类型])
断言可以扩展为类型查询语法,只能与 switch
语法一起使用
switch [v :=] 接口变量.(type) {
case 类型1:
...
case *类型2:
...
接口匿名嵌套 & 匿名接口
接口匿名嵌套:
- 接口可以嵌套子接口,实现接口的扩展
- 匿名嵌套的子
interface
中,不能存在多个相同的方法名
匿名接口:
- 匿名接口是定义变量时将类型指定为接口,并包含函数签名
- 匿名接口常用于初始化一次接口变量的场景
package main
import "fmt"
type Fly interface {
Fly(name string) bool
}
type Swim interface {
Swim(name string) bool
}
type Feature interface {
//Fly(name string) bool
//Swim(name string) bool
// 匿名嵌入,效果等同于上面定义
Fly
Swim
// 接口的其他方法
do(name string)
clean()
}
// Duck 实现了 Feature、Swim、Fly 3个接口
type Duck struct {
}
func (d Duck) Fly(name string) bool {
fmt.Println(name, "can fly")
return true
}
func (d Duck) Swim(name string) bool {
fmt.Println(name, "can swim")
return true
}
func (d Duck) do(name string) {
d.Fly(name)
d.Swim(name)
}
func (d Duck) clean() {
fmt.Println("clean")
}
func main() {
// 匿名嵌套
d := Duck{}
// 游玩好后,延迟清理
defer d.clean()
var fly Fly = d
var swim Swim = d
var feature Feature = d
fmt.Printf("%T %#v %T %#v %T %#v\n", fly, fly, swim, swim, feature, feature)
fly.Fly("tanglaoya")
swim.Swim("tanglaoya")
feature.do("tanglaoya")
// 匿名接口
var chicken interface {
Fly(name string) bool
}
chicken = fly
chicken.Fly("xiaoji")
/* Output:
main.Duck main.Duck{} main.Duck main.Duck{} main.Duck main.Duck{}
tanglaoya can fly
tanglaoya can swim
tanglaoya can fly
tanglaoya can swim
xiaoji can fly
clean
*/
}
空接口
定义:
interface{}
- 空接口是不包含任何函数签名的接口(默认所有类型均实现了0个结构体方法)
- 作用:
- 空接口声明的变量可以接受任何类型变量的赋值
- 一般使用在函数参数中,接受任意类型参数,结合可变参数,接受任意长度的可变参数
package main
import "fmt"
// 空接口
type Empty interface {
}
// 空结构体
type EmptyStruct struct {
}
// 接收任意类型的可变参数
func test(args ...interface{}) {
for i, arg := range args {
fmt.Println(i, ":", arg)
switch v := arg.(type) {
case int:
fmt.Println("int", v)
case string:
fmt.Println("string", v)
case bool:
fmt.Println("bool", v)
default:
fmt.Printf("%T %#v", v, v)
}
}
}
func main() {
var es EmptyStruct
es = EmptyStruct{}
var e Empty
fmt.Printf("%T %#v %T %#v\n", es, es, e, e) // main.EmptyStruct main.EmptyStruct{} <nil> <nil>
e = "hello"
fmt.Println(e)
e = true
fmt.Println(e)
e = es
fmt.Println(e)
// 使用一般定义为匿名接口
var v interface{}
fmt.Printf("%T %#v\n", v, v)
v = 1
fmt.Printf("%T %#v\n", v, v)
// 调用能接受任意类型、任意长度的函数
test(1, "hello", false, e)
}
iface
和 eface
都是 Go
中描述接口的底层结构体,区别在于 iface
描述的接口包含方法,而 eface
则是不包含任何方法的空接口:interface{}
。
Golang 内置接口实现
以下都实现 io.Reader 方法,可以相互赋值: