前言

如果说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]

  1. 隐藏具体实现

隐藏具体实现,这个很好理解。比如我设计一个函数给你返回一个 interface,那么你只能通过 interface 里面的方法来做一些操作,但是内部的具体实现是完全不知道的。

  1. 提供拦截点

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}