在go语言中,多态的特性主要是通过接口来实现。
interface可以定义一组方法,但是不能包含方法体。
interface中不可以包含任何变量。
在接口方法实现的时候,参数列表和返回值列表要保持完全一致。
接口体现了程序设计的高内聚低耦合思想。
Go语言中实现接口是基于方法的,而不是基于接口本身(方法只要包含了接口中的方法就实现了该接口)。因此go中的接口是隐式的,跟java是不同的。
接口是约定行为的集合,它定义了对象能做什么,而不关心具体是谁在做。
接口定义的语法如下
type 接口名 interface{
method1(参数列表)返回值列表
method2(参数列表)返回值列表
}
首先需要定义一个接口,这个接口中包含若干个方法。
定义若干个结构体,结构体中实现了接口中的所有方法,则称为该结构体实现了某接口。
在后续的使用中,比如实现某函数或方法,该函数的参数为接口,那么实现了该接口的所有结构体都可以传给该函数,并且根据传入的结构体调用方法。
type Usb interface {
Start()
Stop()
}
type Phone struct{}
func (p Phone) Start() {
fmt.Println("手机开始工作。。。")
}
func (p Phone) Stop() {
fmt.Println("手机结束工作。。。")
}
type Computer struct{}
func (c Computer) Start() {
fmt.Println("电脑开始工作---")
}
func (c Computer) Stop() {
fmt.Println("电脑结束工作---")
}
func Working(usb Usb) {
usb.Start()
usb.Stop()
}
func main() {
var c Computer
var p Phone
Working(c)
Working(p)
}
输出为
电脑开始工作---
电脑结束工作---
手机开始工作。。。
手机结束工作。。。
1. 接口本身不能创建实例,但是可以指向一个实现了该接口的自定义类型。
type Usb interface {
Start()
Stop()
}
func main() {
var u Usb
u.Start()
u.Stop()
}
不能编译通过。。。
以下这样是可以的,当然前提是Phone实现了Usb接口。
func main() {
var p Phone
var u Usb = p
u.Start()
u.Stop()
}
2. 接口中的所有方法都没有方法体。
3. 某个结构体需要实现一个接口中的所有方法,我们才说这个自定义类型实现了该接口。
4. 只要是自定义结构体,都可以实现接口,而不一定是结构体。
type integer int
这种也属于是自定义类型
5. 一个自定义类型可以实现多个接口。(实现的方法够多就覆盖多个借口了)
6. 如果A接口继承多个接口,这时如果某个自定义类型需要实现该接口,就必须将A接口中的所有方法都实现。
type BInterface interface{
test1()
}
type CInterface interface{
test2()
}
type AInterface interface{
BInterface
CInterface
test3()
}
type Stu struct{
}
func (stu Stu) test1(){
}
func (stu Stu) test2(){
}
func (stu Stu) test3(){
}
该案例中,Stu结构体实现了A、B、C三个接口
7. interface类型默认是一个指针(引用类型),如果没有对interface进行初始化,那么会输出为nil。
8. 空接口interface{}没有任何方法,因此所有的方法都实现了该接口。可以将任何一个变量赋值给一个空接口。
9. 以下代码是错误的
type AInterface interface{
test01()
test02()
}
type BInterface interface{
test01()
test03()
}
type CInterface interface{
AInterface
BInterface
}
func main(){
}
CInterface接口继承了AInterface和BInterface,且AInterface和BInterface中包含两个同名函数test01(),报错。
10. 如果一个自定义类型实现接口的方法,接受参数是指针,那么只能将指针传给接口变量。
package main
import (
"fmt"
)
type Usb interface {
Say()
}
type Stu struct{
}
func (stu *Stu) Say(){
fmt.Println("Say()")
}
func main(){
var stu Stu
var u Usb = stu 编译错误
var u Usb = &stu 正确用法
u.Say()
}
自定义结构体实现了官方函数的接口,就可以调用系统官方函数。
package main
import (
"fmt"
"math/rand"
"sort"
)
type Hero struct {
Name string
Age int
}
type HeroSlice []Hero
func (hs HeroSlice) Len() int {
return len(hs)
}
func (hs HeroSlice) Less(i, j int) bool {
return hs[i].Age < hs[j].Age
}
func (hs HeroSlice) Swap(i, j int) {
hs[i], hs[j] = hs[j], hs[i]
}
func main() {
// 对一个结构体进行排序
// 可以使用系统提供的方法
var heroes HeroSlice
for i := 0; i < 10; i++ {
hero := Hero{
Name: fmt.Sprintf("英雄%d", rand.Intn(100)),
Age: rand.Intn(100),
}
heroes = append(heroes, hero)
}
for _, v := range heroes {
fmt.Println(v)
}
fmt.Println("--------------------")
sort.Sort(heroes)
for _, v := range heroes {
fmt.Println(v)
}
}
该例子中HeroSlice类型实现了sort.Interface(Len()函数、Less()函数、Swap()函数)
因此可以直接sort.Sort(heroes)这样进行使用。
继承的价值在于:解决代码的复用性和可维护性。
接口的价值在于:设计(这个太抽象了)
扩展一下,接口的价值体现在以下三个方面:
1. 跨模块通信:类似于积木不同模块之间的拼接,需要一个接口连接。
比如:
2. 多态:统一处理多种不同类型。
对多个不同类型做相同操作,接口可以统一他们的行为。
3. 封装:对外隐藏实现细节。