最早对interface的认知比较片面,很多人都说interface与channel是go语言的灵魂。然而在工作中使用的机会比较少,发现自己对interface的理解太片面。下面就记录并总结下go中的interface。
interface是个啥
go程序设计中是这样解释的“接口是一种抽象类型,他并没有暴露所包含数据的布局或者内部结构,当然也没有那些数据的基本操作,它所提供的仅仅是一些方法而已”。根据上面的说明我们可以提炼出接口是对一些方法的封装。
很多的C++程序员吐槽go不便之处就是没有’泛型’我们没法使用模板啊,go语言中确实没有,但是go提供了interface让我们可以实现这种泛型。正如上面所说interface是一种抽象类型,是一组方法的集合,我们可以把它看成一种定义内部方法的动态数据类型,任意实现了这些方法的数据类型都可以认为是特定的数据类型。(是不是有点像是C++中模板类)。
正如sort包中
package sort
// A type, typically a collection, that satisfies can be
// sorted by the routines in this package. The methods require that the
// elements of the collection be enumerated by an integer index.
type Interface interface {
// Len is the number of elements in the collection.
Len() int
// Less reports whether the element with
// index i should sort before the element with index j.
Less(i, j int) bool
// Swap swaps the elements with indexes i and j.
Swap(i, j int)
}
在sort包中提供了三个方法,只要我们实现了这三个方法我们就可以对任何的类型进行排序。
看过了官方的sort是如何写的我们也自己动手写一个例子
package main
import "fmt"
type classmate struct {
class uint32
grade uint32
age uint32
name string
}
type info interface {
Len() int
Compare(agex,agei uint32)uint32
}
type listInfo []classmate
type listuint []uint32
func (this listInfo)Len()(int){
return len(this)
}
func (this listInfo)Compare(i,j uint32)(uint32){
if this[i].grade > this[j].grade{
return this[i].grade
}
return this[j].grade
}
func (this listuint)Len()(int){
return len(this)
}
func (this listuint)Compare(i,j uint32)(uint32){
if this[i] > this[j]{
return this[i]
}
return this[j]
}
func main(){
lInfo := listInfo{
classmate{1,1,6,"tom"},
classmate{2,3,8,"jim"},
}
("len is ", ())
garde := (0,1)
("grade is %d \n",garde)
uInfo := listuint{9,6,8}
("len is ", ())
("compare is %d ",(1,2))
}
示例中我们的interface中包含了两个方法,len与compare,获取长度与比较大小,我们分别为不同的类型实现了接口中的方法,是不是有点像C++中的函数重载(C++中函数重载要求参数个数或者参数类型不同)。同时我们也发现我们为不同的类型实现了方法,所以说一个接口类型不会去关心到底是什么数据类型实现了他自身。接口类型的本质就是如果一个数据类型实现了自身的方法集,那么该接口类型变量就能够引用该数据类型的值。
在上面我们提到了sort包,我们只要实现了其提供的三个方法就可以对任何类型进行排序,因为我们实现了它的接口类型。
package main
import "fmt"
type classmate struct {
class uint32
grade uint32
age uint32
name string
}
type classinfo struct {
total uint32
grade uint32
}
type info interface {
coutInfo()
changeInfo(grade uint32)
}
func (this *classmate)coutInfo(){
("info is ",this)
}
func (this *classmate)changeInfo(grade uint32){
= grade
("info is ",this)
}
func (this *classinfo)coutInfo(){
("info is ",this)
}
func (this *classinfo)changeInfo(grade uint32){
= grade
("info is ",this)
}
func interTest(test info,grade uint32){
()
(grade)
}
func main(){
mate := &classmate{
class: 1,
grade:1,
age:6,
name:"jim",
}
info := &classinfo{
total:30,
grade:5,
}
interTest(mate,3)
interTest(info,8)
}
输出:
info is &{1 1 6 jim}
info is &{1 3 6 jim}
info is &{30 5}
info is &{30 8}
相信这个例子可以帮助我们更好的理解函数interTest的第一个输入参数并没有要求参数的具体类型,而是一个接口类型。
使用interface的注意事项
- 将对象赋值给接口变量时会复制该对象。
- 接口使用的是一个名为itab的结构体存储的
type iface struct{
tab *itab // 类型信息
data // 实际对象指针
} - 只有接口变量内部的两个指针都为nil的时候,接口才等于nil。