谈谈golang中的interface
前言
如果说goroutine和channel是Go并发的两大基石,那么接口是Go语言编程中数据类型的关键。在Go语言的实际编程中,几乎所有的数据结构都围绕接口展开,接口是Go语言中所有数据结构的核心。
Go不是一种典型的OO语言,它在语法上不支持类和继承的概念。
没有继承是否就无法拥有多态行为了呢?答案是否定的,Go语言引入了一种新类型—Interface,它在效果上实现了类似于C++的“多态”概念,虽然与C++的多态在语法上并非完全对等,但至少在最终实现的效果上,它有多态的影子。
虽然Go语言没有类的概念,但它支持的数据类型可以定义对应的method(s)。本质上说,所谓的method(s)其实就是函数,只不过与普通函数相比,这类函数是作用在某个数据类型上的,所以在函数签名中,会有个receiver(接收器)来表明当前定义的函数会作用在该receiver上。
Go语言支持的除Interface类型外的任何其它数据类型都可以定义其method(而并非只有struct才支持method),只不过实际项目中,method(s)多定义在struct上而已。从这一点来看,我们可以把Go中的struct看作是不支持继承行为的轻量级的“类”。
从语法上看,Interface定义了一个或一组method(s),这些method(s)只有函数签名,没有具体的实现代码。
为何要接口
在Gopher China上,有大神给出了如下三点理由: - writing generic algorithm (泛型编程) - hiding implementation detail (隐藏具体实现) - providing interception points (提供拦截点) 1. 严格的说,在Golang中并不支持泛型编程,但是使用interface我们可以实现泛型编程
package main
import (
"fmt"
"sort"
)
type Person struct {
Name string
Age int
}
func (p Person) String() string {
return fmt.Sprintf("%s: %d", p.Name, p.Age)
}
// ByAge实现sort.Interface 为[]Person里面的age字段
type ByAge []Person //自定义
// 下面三个方法是实现sort.Interface里面的方法签名
func (a ByAge) Len() int {
return len(a)
}
func (a ByAge) Swap(i, j int) {
a[i], a[j] = a[j], a[i]
}
func (a ByAge) Less(i, j int) bool {
return a[i].Age < a[j].Age
}
func main() {
people := []Person{
{"Bob", 31},
{"John", 42},
{"Michael", 17},
{"Jenny", 26},
}
fmt.Println(people)
sort.Sort(ByAge(people))
fmt.Println(people)
}
// output:
// [Bob: 31 John: 42 Michael: 17 Jenny: 26]
// [Michael: 17 Jenny: 26 Bob: 31 John: 42]
- 隐藏具体实现
隐藏具体实现,这个很好理解。比如我设计一个函数给你返回一个 interface,那么你只能通过 interface 里面的方法来做一些操作,但是内部的具体实现是完全不知道的。
- 提供拦截点
interface源码分析
根据interface是否包含有method方法签名,底层实现上用了2种struct结构体表示:
- eface: 不含method的interface结构,或者叫empty interface;
- iface: 含method的interface结构。
示例如下
不使用接口
package main
import "fmt"
type person struct {
first string
}
func (p person) speak() {
fmt.Println(p.first)
}
func main() {
p1:=person{"Wang 1"}
p2:=person{"Wang 2"}
p1.speak()
p2.speak()
}
上面程序将会输出
Wang 1
Wang 2
使用接口和不使用接口混用对比
package main
import "fmt"
type human interface {
speak()
//walk()
}
type person struct {
first string
}
func (p person) speak() {
fmt.Println(p.first)
}
type car struct {
color string
}
func (c car) speak() {
fmt.Println("I am color:", c.color)
}
// 函数,传入一个接口
func foo(h human) {
fmt.Println(h)
}
func main() {
p1 := person{"Wang 1"}
p2 := person{"Wang 2"}
c1 := car{"red"}
p1.speak()
p2.speak()
c1.speak()
foo(p1)
foo(p2)
}
输出
Wang 1
Wang 2
I am color: red
{Wang 1}
{Wang 2}