1. Golang中的接口

在Go语言中接口(interface)是一种类型,一种抽象的类型。

接口(interface)定义了一个对象的行为规范, 只定义规范不实现 ,由具体的对象来实现规范的细节 。

实现接口的条件:

一个对象只要全部实现了接口中的方法 ,那么就实现了这个接口。

换句话说,接口就是一个需要实现的方法列表。

2. 为什么要使用接口

下面的代码中定义了猫和狗,然后它们都会叫,你会发现main函数中明显有重复的代码

如果我们后续再加上猪、青蛙等动物的话,我们的代码还会一直重复下去

那我们能不能把它们当成“能叫的动物”来处理呢?

type Cat struct {
Name string
}

func (c Cat) Say() string {
return c.Name + ":喵喵喵"
}

type Dog struct {
Name string
}

func (d Dog) Say() string {
return d.Name + ": 汪汪汪"
}

func main() {
c := Cat{Name: "小白猫"} // 小白猫:喵喵喵
fmt.Println(c.Say())
d := Dog{"阿黄"}
fmt.Println(d.Say()) // 阿黄: 汪汪汪
}
/*
小白猫:喵喵喵
阿黄: 汪汪汪
*/

3. 定义一个Usber接口

定义一个 Usber 接口让 Phone 和 Computer 结构体实现这个接口

//1.接口是一个规范
type Usber interface {
getName() string
}

//2.如果接口里面有方法的话,必要要通过结构体或者通过自定义类型实现这个接口
type Phone struct {
Name string
}

type Computer struct {
Brand string
}

func (c *Computer) getName() string {
return c.Brand
}

//3.手机要实现usb接口的话必须得实现usb接口中的所有方法
func (p *Phone) getName() string {
return p.Name
}

func main() {
p := &Phone{
Name: "华为手机",
}
c := &Computer{
Brand: "联想电脑",
}
var p1 Usber // golang中接口就是一个数据类型
p1 = p // 表示手机实现Usb接口
fmt.Println(p1.getName())
//接口使用场景,处理相同类型的数据
newName := transData(p)
newName1 := transData(c)
fmt.Println(newName, newName1)
}

func transData(usber Usber) string {
name := usber.getName()
return fmt.Sprintf("%s%s", name, "处理后")
}

当我们要去处理同一数据类型的时候这个数据的时候,比如猫 狗是同一类数据类型。

像k8s的deployment,pod,configmap,secretd等等这些资源的时候,它都是k8s的一个资源,在获取列表的时候会对其做数据处理,比如要对其进行数据排序,那么只需要对这个接口进行排序就行了,不需要对每个资源都去写一遍排序的方法,那么这样不是很冗余吗?

空接口

1. 空接口说明

  • Golang中空接口也可以直接当做类型来使用,可以表示任意类型 (泛型概念,最大的特点)
  • Golang 中的接口可以不定义任何方法,没有定义任何方法的接口就是空接口。
  • 空接口表示没有任何约束,因此任何类型变量都可以实现空接口。
  • 空接口在实际项目中用的是非常多的,用空接口可以表示任意数据类型

2. 空接口作为函数的参数

//空接口作为函数的参数
func show(a interface{}) {
fmt.Printf("值:%v 类型:%T\n", a, a) }
func main() {
show(20) // 值:20 类型:int
show("你好golang") // 值:你好golang 类型:string
slice := []int{1, 2, 34, 4}
show(slice) // 值:[1 2 34 4] 类型:[]int
}

printf println是可以接受任何参数的,func Printf(format string , a ...interface{})可以看到这里可以接受任何空接口类型,...类似于切片类型。

func test(a int, b ...interface{}) {
for _, v := range b {
fmt.Printf("%v,%T \n", v, v)
}
}

func main() {
test(1, "a", 22, []string{"1"})
}

a,string
22,int
[1],[]string

3. 切片实现空接口

切片和map是同一类型的元素,有了空接口,在同一个切片同一个map中就可以将任何类型的数据放进去。

func main() { 
var slice = []interface{}{"张三", 20, true, 32.2}
fmt.Println(slice) // [张三 20 true 32.2]
}

4. map 的值实现空接口

func main() { // 空接口作为 map 值 
var studentInfo = make(map[string]interface{})
studentInfo["name"] = "张三"
studentInfo["age"] = 18 studentInfo["married"] = false
fmt.Println(studentInfo) // [age:18 married:false name:张三] }
类型断言
  • 一个接口的值(简称接口值)是由一个具体类型和具体类型的值两部分组成的。
  • 这两部分分别称为接口的动态类型和动态值。(一个空接口,可以是结构体,指针,布尔各种类型。它其实是动态的,你传递什么都可以,这就叫做动态类型和动态值)
  • 如果我们想要判断空接口中值的类型,那么这个时候就可以使用类型断言
  • 其语法格式: x.(T)

x : 表示类型为 interface{}的变量

T : 表示断言 x 可能是的类型

断言返回两个值,一个是断言是否转化成果,一个是转化结果,注意空接口类型是空接口类型。

var str interface{}
str = "hello"
str = str + "sss" //这样写是会报错的,因为空接口类型不是字符串类型,需要断言转化才可以
type test1 struct {
name string
age int
}

func main() {
var t interface{}
//未断言前没有结构体的特征,需要断言转化为结构体指针
t = &test1{
name: "hh",
age: 12,
}

v, ok := t.(*test1) //这里其实就是做了一个类型转化,这里的类型就变为来结构体指针类型

if ok {
fmt.Printf("类型:%T 值:%#v\n", v, v)
} else {
fmt.Println("xxx")
}
}

类型:*main.test1 值:&main.test1{name:"hh", age:12}