📖Golang中接口的设计和实现

发布: 2021-07-30
热度: 38
趋势: 38
权重: 0
🎯

Go语言中的接口采用的是隐式实现,不需要去申明实现,只需要直接实现接口所定义的全部方法即可,同时区分了直接实现与指针实现两种形态,在实际使用时需要注意和关注

接口

接口是面向对象编程实现多态的基操。

在绝大多数情况下,数据可能是不同类型,不同类型却可能存在着一个和多个共通点。

这些共通点就是抽象的基础。

接口的本质就是某一个抽象的共通点,实际类型可能是一对多(一个类型实现多个接口)或多对一(多种类型实现一个接口),这就是多态的体现。

举个例子:

  • 老王和老张两个好兄弟是两个具体的类型,我们提取他们身上可能存在的共通点。
  • 他们都是人,那么在编程世界中,人是可以作为一个接口存在的。
  • 人都需要睡觉,所以睡觉可以作为一个接口方法存在。
  • 老王喜欢趴着睡,就相当于老王的睡觉方法的具体操作是趴着,相当于接口方法的实现。
  • 老张喜欢侧着睡,就相当于老张的睡觉方法的具体操作是侧着,相当于接口方法的实现。

从编程的角度来看,如果调取人这个接口让其执行睡觉方法,最终结果就是老王趴着睡、老张侧着睡。

所以,接口是什么?

接口是一组包含方法名(入参、返回值)的未进行具体实现的方法集。

判断一个类型是否实现了接口,就看该类型是否全部实现了接口所定义的方法。

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.(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 空接口

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

既然空接口可以接受任意类型的数据,那自然会涉及到如何区分类型的需要。

目前有两种方式去区分:断言、判断。

Comma-ok 断言

相当于对上方 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 判断

将语法改成 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
}
当前文章暂无讨论,留下脚印吧!
大纲
  • 接口
  • Go 接口特点
    • 接口实现
    • 指针实现
      • x.(T)验证
  • Go 空接口
    • Comma-ok 断言
    • Switch 判断
  • 嵌套接口
提交成功,请等待审核通过后全面展示!

发表评论

昵称
邮箱
链接
签名
评论

温馨提示:系统将通过浏览器临时记忆您曾经填写的个人信息且支持修改,评论提交后仅自己可见,内容需要经过审核后方可全面展示。

选择头像