文章目录
Go 语言接口
接口(interface)定义了一个对象的行为规范,只定义规范不实现,由具体的对象来实现规范的细节。
interface是一组method的集合,是duck-type programming的一种体现。接口做的事情就像是定义一个协议(规则),只要一台机器有洗衣服和甩干的功能,我就称它为洗衣机。不关心属性(数据),只关心行为(方法)。
为了保护你的Go语言职业生涯,请牢记接口(interface)是一种类型。
interface类型可以定义一组方法,但是不需要实现。并且接口(interface)不能包含任何变量。
实例
go
/* 定义接口 */
type interface_name interface {
method_name1 [return_type]
method_name2 [return_type]
method_name3 [return_type]
...
method_namen [return_type]
}
/* 定义结构体 */
type struct_name struct {
/* variables */
}
/* 实现接口方法 */
func (struct_name_variable struct_name) method_name1() [return_type] {
/* 方法实现 */
}
...
func (struct_name_variable struct_name) method_namen() [return_type] {
/* 方法实现*/
}
例子
Go 中的接口是满足隐式实现 的(这一点是非常坑人的,建议阅读 Go语言令人厌恶的设计)。 Go 不提供用于实现接口的关键字。
go
package main
import (
"fmt"
"math"
)
func main() {
var s Shape = Square{3}
printInformation(s)
c := Circle{6}
printInformation(c)
}
//打印面积和周长
func printInformation(s Shape) {
fmt.Printf("%T\n", s)
fmt.Println("Area: ", s.Area())
fmt.Println("Perimeter:", s.Perimeter())
fmt.Println()
}
//---Shape 形状---
type Shape interface {
//周长
Perimeter() float64
//面积
Area() float64
}
//---Square 正方形---
type Square struct {
size float64
}
func (s Square) Area() float64 {
return s.size * s.size
}
func (s Square) Perimeter() float64 {
return s.size * 4
}
//---Circle 圆形---
type Circle struct {
radius float64
}
func (c Circle) Area() float64 {
return math.Pi * c.radius * c.radius
}
func (c Circle) Perimeter() float64 {
return 2 * math.Pi * c.radius
}
输出
main.Square
Area: 9
Perimeter: 12
main.Circle
Area: 113.0973
Perimeter: 37.6991
空接口
空接口的定义
空接口是指没有定义任何方法的接口。因此任何类型都实现了空接口。
空接口类型的变量可以存储任意类型的变量。
go
func main() {
// 定义一个空接口x
var x interface{}
s := "pprof.cn"
x = s
fmt.Printf("type:%T value:%v\n", x, x)
i := 100
x = i
fmt.Printf("type:%T value:%v\n", x, x)
b := true
x = b
fmt.Printf("type:%T value:%v\n", x, x)
}
空接口的应用
空接口作为函数的参数
使用空接口实现可以接收任意类型的函数参数。
go
// 空接口作为函数参数
func show(a interface{}) {
fmt.Printf("type:%T value:%v\n", a, a)
}
空接口作为map的值
使用空接口实现可以保存任意值的字典。
go
// 空接口作为map值
var studentInfo = make(map[string]interface{})
studentInfo["name"] = "李白"
studentInfo["age"] = 18
studentInfo["married"] = false
fmt.Println(studentInfo)
类型断言
空接口可以存储任意类型的值,那我们如何获取其存储的具体数据呢?
接口值
一个接口的值(简称接口值)是由一个具体类型和具体类型的值两部分组成的。这两部分分别称为接口的动态类型和动态值。
我们来看一个具体的例子:
go
var w io.Writer
w = os.Stdout
w = new(bytes.Buffer)
w = nil
请看下图分解:
想要判断空接口中的值这个时候就可以使用类型断言,其语法格式:
x.(T)
其中:
x:表示类型为interface{}的变量
T:表示断言x可能是的类型。
该语法返回两个参数,第一个参数是x转化为T类型后的变量,第二个值是一个布尔值,若为true则表示断言成功,为false则表示断言失败。
举个例子:
go
func main() {
var x interface{}
x = "pprof.cn"
v, ok := x.(string)
if ok {
fmt.Println(v)
} else {
fmt.Println("类型断言失败")
}
}
上面的示例中如果要断言多次就需要写多个if判断,这个时候我们可以使用switch语句来实现:
go
func justifyType(x interface{}) {
switch v := x.(type) {
case string:
fmt.Printf("x is a string,value is %v\n", v)
case int:
fmt.Printf("x is a int is %v\n", v)
case bool:
fmt.Printf("x is a bool is %v\n", v)
default:
fmt.Println("unsupport type!")
}
}
因为空接口可以存储任意类型值的特点,所以空接口在Go语言中的使用十分广泛。
关于接口需要注意的是,只有当有两个或两个以上的具体类型必须以相同的方式进行处理时才需要定义接口。不要为了接口而写接口,那样只会增加不必要的抽象,导致不必要的运行时损耗。
类型断言例子001
类型断言也可以判断指针类型
go
package main
import "fmt"
func main() {
var n1 int32 = 30
var n2 string = "tomcat"
n3 := false
stu1 := Student{}
stu2 := &Student{}
justifyType(n1,n2,n3,stu1,stu2)
}
type Student struct {
}
func justifyType(items ...interface{}) {
for index, x := range items {
switch v := x.(type) {
case string:
fmt.Printf("第%v个参数是 string 类型,值是 %v\n",index,v)
case int:
fmt.Printf("第%v个参数是 int 类型,值是 %v\n",index,v)
case bool:
fmt.Printf("第%v个参数是 bool 类型,值是 %v\n",index,v)
case Student:
fmt.Printf("第%v个参数是 Student 类型,值是 %v\n",index,v)
case *Student:
fmt.Printf("第%v个参数是 *Student 类型,值是 %v\n",index,v)
default:
fmt.Println("unsupport type!")
}
}
}
类型断言例子002
go
package main
import "fmt"
func main() {
var i interface{} = "Hello, World"
str, ok := i.(string)
if ok {
fmt.Printf("'%s' is a string\n", str)
} else {
fmt.Println("conversion failed")
}
}
类型断言例子003
给Phone结构体一个特有的方法Call(),当Usb接口接收的是Phone变量时,还需要调用call方法
go
package main
import "fmt"
func main() {
//定义一个usb接口数组,可以存放Phone和Camera的结构体变量
// 这里就体现出多态数组
var usbArr [3]Usb
usbArr[0] = Phone{"vivo"}
usbArr[1] = Phone{"小米"}
usbArr[2] = Camera{"惠普"}
//遍历usbArr
var computer Computer
for _, v := range usbArr {
computer.Working(v)
}
}
type Usb interface {
Start()
Stop()
}
type Camera struct {
name string
}
type Phone struct {
name string
}
type Computer struct {
name string
}
func (c Camera) Start() {
fmt.Println("相机开始工作...")
}
func (c Camera) Stop() {
fmt.Println("相机停止工作...")
}
func (p Phone) Start() {
fmt.Printf("%s 手机开始工作...\n", p.name )
}
func (p Phone) Stop() {
fmt.Printf("%s 手机停止工作...\n", p.name )
}
func (p Phone) Call() {
fmt.Println("手机 打电话")
}
func (computer Computer) Working(usb Usb) {
usb.Start()
if phone, ok := usb.(Phone); ok {
phone.Call()
}
usb.Stop()
}
巩固练习
- 在Go语言中,接口是什么?请解释其含义。
- 如何定义一个接口?解释接口定义的基本语法。
- Go语言中的接口实现是如何工作的?请详细解释。
- 什么是空接口?它有什么用途?
- 能否给出一些使用接口的例子?这些例子中接口的作用是什么?
- Go语言不同接口、声明了同名方法,怎么解决问题?