Go interface接口

本文最后更新于:9 天前

1 接口的介绍

1.1 现实生活中的接口

比如手机电脑相机,都是可以通过接口来进行连接的,我们不需要关注卡槽的大小,因为所有的usb接口都是按照统一的标准来设计的

1.2 Golang的接口

在Golang中接口(interface)是一种类型,一种抽象的类型,接口是一组函数method的结合,Golang中的接口不能包含任何变量,可以认为它是一种定义接口的规范。

1.3 Goalng 接口的定义

在Golang中接口中的所有方法都没有方法体,接口定义了一个对象的行为规范,只是定义规范不实现,接口体现了程序设计的多态和高内聚低耦合的思想

Gloang中的接口也是一种数据类型,不需要显示实现,只需要一个变量含有接口类型的所有方法,那么这个变量就是实现了这个接口

Golang中每个接口由数个方法组成,接口的定义格式如下:

type 接口名字 interface {
	方法名字1(参数列表1)  返回值列表1
	方法名字2(参数列表2)  返回值列表2
	...
}
  • 接口名字: 使用type 关键字将接口定义为自定义的类型名, Go语言的接口在命名的时候,一般会在单词后面添加er,如写操作的接口叫做Writer,有字符串功能的接口叫Stinger等,接口名字最好可以见名知意
  • 方法名字: 当方法名字首字母大写且接口类型首字母大写的时候,可以被接口所在的包之外的代码访问
  • 参数列表,返回值列表: 这个是可以省略的,跟函数的是一样的。

1.4 接口的使用

代码演示

package main

import "fmt"

type Usber interface {
	start()
	stop()
}

type iphone struct {
	name string
}

type camera struct {
	name string
}

//iphone 结构体实现 Usber接口的俩个方法 一个start 一个stop
func (i iphone) start() {
	fmt.Printf("%v 插入usb 成功\n", i.name)
}
func (i iphone) stop() {
	fmt.Printf("%v 拔出usb 成功\n", i.name)
}

//camera 结构体实现 Usber接口的俩个方法 一个start 一个stop
func (c camera) start() {
	fmt.Printf("%v 插入usb 成功\n", c.name)
}
func (c camera) stop() {
	fmt.Printf("%v 拔出usb 成功\n", c.name)
}
func main() {
	var i1 = iphone{
		name: "xiaomi",
	}
	var c1 = camera{
		name: "canon",
	}
	var u1 Usber
	u1 = i1
	u1.start()
	u1.stop()
	var u2 Usber
	u2 = c1
	u2.start()
	u2.stop()
}
/*
xiaomi 插入usb 成功
xiaomi 拔出usb 成功

canon 插入usb 成功
canon 拔出usb 成功
*/

通过电脑空结构体实现调用接口方法

package main

import "fmt"

type Usber1 interface {
	start()
	stop()
}
type computer struct {
}
type iphone struct {
	name string
}
type camera struct {
	name string
}

func (com computer) worker(usb Usber1) {
	usb.start()
	usb.stop()
}

//iphone 结构体实现 Usber接口的俩个方法 一个start 一个stop
func (i iphone) start() {
	fmt.Printf("%v 插入电脑usb 成功\n", i.name)
}
func (i iphone) stop() {
	fmt.Printf("%v 拔出电脑usb 成功\n", i.name)
}

//camera 结构体实现 Usber接口的俩个方法 一个start 一个stop
func (c camera) start() {
	fmt.Printf("%v 插入电脑usb 成功\n", c.name)
}
func (c camera) stop() {
	fmt.Printf("%v 拔出电脑usb 成功\n", c.name)
}
func main() {
	var i1 = iphone{
		name: "ihpone12",
	}
	var c1 = camera{
		name: "sony 50D",
	}
	var com1 computer
	var inter1 Usber1 = i1
	var inter2 Usber1 = c1
	com1.worker(inter1)
	com1.worker(inter2)

}
/*
ihpone12 插入电脑usb 成功
ihpone12 拔出电脑usb 成功

sony 50D 插入电脑usb 成功
sony 50D 拔出电脑usb 成功
*/

2 空接口介绍

golang中的空接口,可以通过接口方式定义,也可以直接当做类型来使用,可以表示任意数据类型

2.1 通过接口的方式定义

type Aer interface{}  //表示一个空接口
var str = "hello"
var A1 Aer
A1  := str
fmt.Printf("值:%v 类型: %T",A1,A1) //值:hello 类型: string

2.2 直接通过类型来定义

var a  interface{}
a = 20 
fmt.Printf("值:%v 类型: %T",a,a)
a = "你好"
fmt.Printf("值:%v 类型: %T",a,a)
a = true
fmt.Printf("值:%v 类型: %T",a,a)

3 空接口的使用

3.1 空接口作为函数的使用

func show(a interface{}) {
	fmt.Printf("值:%v 类型: %T", a, a)
}

3.2 map的值实现空接口

这样我们map类型的值,就可以是接收任意值(不像以前必须是定义死的,比如string)

var UserInfo = make(map[string]interface{})
UserInfo["name"] = "bds"
UserInfo["married"] = false
UserInfo["age"] = 18

3.3 切片实现空接口

切片同理也可以接收任意类型

var slice = []interface{}{"bds",20,true}

4 类型断言

一个接口的值(接口值) 是由一个具体类型和具体类型的值俩部分组成,这俩个部分分别称为接口的动态类型和动态值

如果我们想要判断空接口中值的类型到底是什么类型 ,这个时候我们就要使用类型断言

x(T)
  • x: 表示类型为interface{}的变量
  • T:表示断言x 可能是的类型

该语法返回俩个值,第一个参数是x转化为T类型后的变量,第二值是一个布尔值 ,返回true表示断言成功,否则失败

package main

import "fmt"
//第一种方法通过i.(T)实现
func MyPrint(i interface{}) {
	if _, ok := i.(string); ok {
		fmt.Printf("%v是%T\n", i, i)
	} else if _, ok := i.(int); ok {
		fmt.Printf("%v是%T\n", i, i)
	} else if _, ok := i.(bool); ok {
		fmt.Printf("%v是%T\n", i, i)
	} else {
		fmt.Println("断言失败")
	}

}
//第二种方法通过i.(type) 只能结合switch 使用 
func MyPrint2(i interface{}) {
	switch i.(type) {
	case int:
		fmt.Printf("%v是%T\n", i, i)
	case string:
		fmt.Printf("%v是%T\n", i, i)
	case bool:
		fmt.Printf("%v是%T\n", i, i)
	default:
		fmt.Println("对不起,我不认识")
	}
}

func main() {
	var a string = "bds"
	var b int = 10
	var c bool = true
	MyPrint(a)
	MyPrint(b)
	MyPrint(c)
	var d = 12.23
	MyPrint2(a)
	MyPrint2(b)
	MyPrint2(c)
	MyPrint2(d)
}
/*
bds是string
10是int
true是bool

bds是string
10是int
true是bool
对不起,我不认识

*/

上面我们通过电脑调用接口方法我们可以来通过断言判断, 比如是如果手机结构体 值调用start 方法。

func (com computer) worker(usb Usber1) {
	if _, ok := usb.(iphone); ok {
		usb.start()
	} else {
		usb.stop()

	}
}

5 结构体 值和指针 接收者的区别

5.1 值接收者

实例化后的结构体 值类型指针类型都可以赋值给 接口变量

package main

import (
	"fmt"
)

type Usber3 interface {
	start()
	stop()
}
type iphone1 struct {
	name string
}
//值类型接收传入参数
func (i iphone1) start() {
	fmt.Printf("%v start\n", i.name)
}
func (i iphone1) stop() {
	fmt.Printf("%v stop", i.name)
}
func main() {
	var tel1 = iphone1{
		name: "xiaomi",
	}
	var u1 Usber3 = tel1
	u1.start()
	//定义指针变量传入  
	var tel2 = &iphone1{ 
		name: "oneplus",
	}
	var u2 Usber3 = tel2
	u2.start()

}

5.2 指针接收者

只能接收指针类型的

package main

import (
	"fmt"
)

type Usber3 interface {
	start()
	stop()
}
type iphone1 struct {
	name string
}

//值类型接收传入参数
func (i iphone1) start() {
	fmt.Printf("%v start\n", i.name)
}
//这里改成指针类型接收传入参数,发现tel1 值类型的传入编译报错 。
func (i *iphone1) stop() {
	fmt.Printf("%v stop", i.name)
}
func main() {
	//var tel1 = iphone1{
	//	name: "xiaomi",
	//}
	//var u1 Usber3 = tel1
	//u1.start()  //报错: iphone1 does not implement Usber3 (stop method has pointer receiver)
	
	//定义指针变量传入 这个依然正常。不会报错
	var tel2 = &iphone1{
		name: "oneplus",
	}
	var u2 Usber3 = tel2
	u2.start()
	u2.stop()  //正常调用stop 方法
}

5.3 实现一个组合接收者类型

package main

import "fmt"

type Animaler interface {
	GetName() string
	SetName(string)
}
type Dog struct {
	Name string
}

func (d Dog) GetName() string {
	return d.Name
}
func (d *Dog) SetName(n string) {
	d.Name = n
}
func main() {
	d1 := &Dog{
		Name: "兰博基尼狗",
	}
	var a1 Animaler = d1
	fmt.Println(a1.GetName())
	a1.SetName("玛莎拉蒂狗")
	fmt.Println(a1.GetName())
}
/*
兰博基尼狗
玛莎拉蒂狗
*/

6 一个结构体实现多个接口

package main

import (
	"fmt"
)

type zoo1 interface {
	getname()
}
type zoo2 interface {
	setname(string)
}
type Cat struct {
	name string
}

func (c Cat) getname() {
	fmt.Println(c.name)
}
func (c *Cat) setname(n string) {
	c.name = n
}
func main() {
	var c = &Cat{
		name: "小花猫",
	}
	var z1 = c
	z1.getname()
	z1.setname("小红猫")
	z1.getname()

}

7 接口的嵌套

package main

import "fmt"

type blue interface {
	getMode()
}
type fuyin interface {
	getColor()
}
//直接嵌套上面俩个接口
type kq interface {
	blue
	fuyin
}
type qin struct {
	name  string
	mode  string
	color string
}

func (q qin) getMode() {
	fmt.Println(q.mode)
}
func (q qin) getColor() {
	fmt.Println(q.color)
}
func main() {
	var q1 = qin{
		name:  "honner",
		mode:  "blues",
		color: "red",
	}
	var k1 kq = q1
	k1.getMode()
	k1.getColor()

}

8 空接口针对 切片 结构体类型的取值

上面空接口会返回动态类型和动态值,这个时候取值是失败的,所以要通过断言的方式判断值到底是什么类型。然后针对类型的使用方法 进行取值

package main

import "fmt"

type Addr struct {
	address string
	iphone  string
}

func main() {
	var user1 = make(map[string]interface{})
	user1["name"] = "bds"
	user1["age"] = 18
	user1["hobby"] = []string{"games", "睡觉"}
	//切片类型
	if hobbyValue, ok := user1["hobby"].([]string); ok {
		fmt.Println(hobbyValue[1])    //睡觉
	}
  //切片类型
	user1["sw"] = make([]string, 3)
	swValue, _ := user1["sw"].([]string)
	swValue[0] = "180"
	swValue[1] = "120"
	//结构体类型
	fmt.Println(user1["sw"]) //[180 120 ]
	var addr1 = Addr{
		address: "bj",
		iphone:  "13310011",
	}

	user1["addr"] = addr1
	addrValue, _ := user1["addr"].(Addr)
	fmt.Println(addrValue.iphone) //13310011
	fmt.Println(addrValue.address) //bj
}

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!