Go语言中的接口采用的是隐式实现,不需要去申明实现,只需要直接实现接口所定义的全部方法即可,同时区分了直接实现与指针实现两种形态,在实际使用时需要注意和关注
接口是面向对象编程实现多态的基操。
在绝大多数情况下,数据可能是不同类型,不同类型却可能存在着一个和多个共通点。
这些共通点就是抽象的基础。
接口的本质就是某一个抽象的共通点,实际类型可能是一对多(一个类型实现多个接口)或多对一(多种类型实现一个接口),这就是多态的体现。
举个例子:
从编程的角度来看,如果调取人这个接口让其执行睡觉方法,最终结果就是老王趴着睡、老张侧着睡。
所以,接口是什么?
接口是一组包含方法名(入参、返回值)的未进行具体实现的方法集。
判断一个类型是否实现了接口,就看该类型是否全部实现了接口所定义的方法。
Go 语言中接口的独特之处在于隐式实现。
具体的类型并不需要声明其实现了哪些接口,实现接口必须的方法即可完成接口的实现。
我们先看看 Java 语言中如何实现一个接口:
// 接口
public interface People {
boolean Sleep();
}
// 实现(类需要显式申明实现接口)
public class PeopleImpl implements People {
@Override
public boolean Sleep() {
System.out.println("睡了!");
return true;
}
}
这里有一个关键字 implements,用来声明实现某接口。
Java 中是类实现接口而不是具体类型。
在 Go 中,不需要声明实现,只需要实现了接口的全部方法,便隐式的实现了接口。
依旧上面那段逻辑,修改成 Go 语言形式去实现。
// 接口
type People interface {
Sleep() bool
}
// 具体类型
type Man struct {
Name string
}
// 类型实现接口方法
func (man Man) Sleep() bool {
fmt.Println("我是" + man.Name + "!我睡了!")
return true
}
上文我们提到,在 Go 中只要实现了接口的全部方法就隐式的实现了接口。
上述代码中,People 接口只有一个 Sleep() 方法,Man 类型实现了该方法,那 Man 类型是否实现了接口 People 呢?
func main() {
// 定义一个接口对象
var p People
// 定义个类型对象
lw := Man{"老王"}
// 将类型赋给接口
p = lw
// 调用接口方法
p.Sleep()
}
输出结果如下:
我是老王!我睡了!
Go 中接口实现并不仅仅是类型直接实现方法,还有指针实现形式。
指针实现的方法尽可以通过指针调用,比如下方代码:
// 具体类型
type Women struct {
Name string
}
// 类型实现接口方法
func (women *Women) Sleep() bool {
fmt.Println("我是" + women.Name + "!我睡了!")
return true
}
func main() {
// 定义一个接口对象
var p People
// 定义个类型对象
lw := Women{"王阿姨"}
// 将类型赋给接口
// 编译报错:Cannot use 'lw' (type Women) as type People Type does not implement 'People' as 'Sleep' method has a pointer receiver
// 提示您 Women 类型并没有实现 People 接口
p = lw
// 将指针赋给接口对象,才能正确实现接口
p = &lw
// 调用接口方法
p.Sleep()
}
可以通过 x.(T) 的方式验证是否已实现,x 只适用于 interface 类型。
// 检查实现
func checkPeople(p interface{}) bool {
if _, ok := p.(People); ok {
return true
}
return false
}
func main() {
// 定义个类型对象
lw := Man{"老王"}
// 判断检查结果
if checkPeople(lw) {
fmt.Println(lw.Name + "是个人!")
} else {
fmt.Println(lw.Name + "不是人!")
}
}
输出结果如下:
老王是个人!
Go 中是可以定义空接口的,比如上文中 checkPeople 方法的入参就是空接口。
因为空接口中不包含任何方法,就意味着任何类型都实现了空接口,所以可以利用空接口存储任意类型的数据。
func main() {
i := 1
s := "string"
var o interface{}
o = i
fmt.Printf("O值:%v", o)
o = s
fmt.Printf("O值:%v", o)
}
输出结果如下:
O值:1
O值:string
既然空接口可以接受任意类型的数据,那自然会涉及到如何区分类型的需要。
目前有两种方式去区分:断言、判断。
相当于对上方 x.(T) 的多重判断。
func checkDataType(p interface{}) {
// 类型强转
if _,ok := p.(People); ok {
fmt.Println("TA是个人!")
} else if _,ok := p.(int); ok {
fmt.Println("TA是个数字!")
} else if _,ok := p.(string); ok {
fmt.Println("TA是个字符串儿!")
}
}
将语法改成 switch 本质没什么区别。
func checkDataType(p interface{}) {
// 获取类型
switch v := p.(type) {
case People:
fmt.Printf("TA是个人!%v", v)
case int:
fmt.Printf("TA是个数字!%v", v)
case string:
fmt.Printf("TA是个字符串儿!%v", v)
}
}
嵌套接口有点类似于继承的感觉,当方法很多的时候,我们新建的接口不一定需要写出所有的方法。
type People interface {
Sleep() bool
}
type WorkMan interface {
GoWork() int
}
// 该接口直接嵌套其他接口形成新的接口
// 表示该接口具备所嵌入接口的全部方法
type Technician interface {
People
WorkMan
}
温馨提示:系统将通过浏览器临时记忆您曾经填写的个人信息且支持修改,评论提交后仅自己可见,内容需要经过审核后方可全面展示。