go中实现接口的几种方式
//定义1个接口student,它包含1个 StudentName 方法
type student interface {
StudentName() string
}
//定义1个结构体
type Boy struct {
Name string
}
//给他这个结构体绑定一个方法 StudentName。这样就表示它实现了 student接口
func (a Boy) StudentName() string {
return a.Name
}
上面是常见的接口申请以及定义1个结构体,然后绑定接口里相同的方法名,这样用以实现接口实现。
值得注意的是,我们定义了1个接口A,里面有个空方法F。然后我们用结构体B绑定方法的方式重写实现了这个空方法F,也就间接的实现了这个接口。一般我们调用的是时候,会直接用原始的接口A的实例去调用这个方法F,而不是这个结构体B的实例去调用F。
理解这一点很重要。
1. 用常规等于实现
我们看下常规是怎么实现的:
//1. 首先,实例化这个结构体
var b1 = Boy{"zhangsan",}
//2. 再实例化一下interface
var s1 student
//3. 将 b1 赋值给 s1,就完成了这个实例化接口的交接仪式
s1 = b1
//打印一下:调用原始的接口的student 的实例s1去调用。虽然b1也可以调用。但是一般不这样用。
fmt.Println(s1.StudentName())
输出:zhangsan
这是我们常见的实现接口的方式,重点是s1 = b1
这个很梦幻的接口实现方式。
2. 通过函数返回值的方式
还有其他几种,也试一下,参考了errors
包实现了error
接口的方式。
//定义1个接口student,它包含1个 StudentName 方法
type student interface {
StudentName() string
}
//定义1个结构体
type Boy struct {
Name string
}
//搞了1个New方法,返回类型写student, 返回内容实例化Boy{name}
func New(name string) student {
return Boy{name}
}
//给他这个结构体绑定一个方法 StudentName。这样就表示它实现了 student接口
func (a Boy) StudentName() string {
return a.Name
}
我们调用一下进行实例化接口,怎么做呢?
b1 := New("lisi")
fmt.Println(b1.StudentName())
//打印:lisi
我们仔细分析一下。这个实现方式,是怎么省去,实现接口必须要用的这一步:s1 = b1
的。
//我们对比下,这种方式和上面的区别
func New(name string) student {
return Boy{name}
}
b1 := New("lisi")
/*-----------------------------------------*/
var b1 = Boy{"zhangsan",}
var s1 student
s1 = b1
-------------------
//既然都能成功,说明,这2种方式是等价的
开始分析:
var b1 = Boy{"zhangsan",}
var s1 student
s1 = b1
b1是Boy类型的变量, s1 是student类型的变量。把 s1 = b1 ,实现了接口。
//这个分解一下:
func New(name string) student {
return Boy{name}
}
// Boy{name} 其实就等价于 b1, 那么就变成了:
func New(name string) student {
return b1
}
再看看返回值,返回值是student类型, 也就是说, b1的类型为student,b1的值为student的一个实例。反过来说,student结构体的1个实例 等于 b1
也就等价于:
//申明1个student实例 s1
var s1 student
//student结构体的1个实例 等于 b1
s1 = b1
------------------------
所以,你看下,是不是和上面原始的赋值一样了:
var b1 = Boy{"zhangsan",}
var s1 student
s1 = b1
------------------------------
非常巧妙,但是新手很难理解。多看看几遍就好了。
3. 通过数组的方式
还是上面这个例子,我们看看如何用数组切片的方式,来实现接口。
b1 := Boy{"zhangsan",}
d11 := []student{b1}
fmt.Println(d11[0].StudentName()) // 打印:zhangsan
又迷惑了?这一顿操作下来,又是怎么把s1=b1
省略的呢?
我们接着分析下:
d11 := []student{b1} 是个数组,它里面子元素的类型为student
也就是等价于:
var b1 student = Boy{"zhangsan",}
正常的应该是这样:
var s1 student
var b1 = Boy{"zhangsan",}
因为要实现接口,就必须要把 s1 = b1
所以也就是: s1和b1 换一个位置。b1 的值也跟着过来了。
于是得到了下面这个:
var b1 student = Boy{"zhangsan",}
就就隐含的实现了 s1 = b1 真的是太巧妙了。
有点烧脑。多看几遍多写几遍就熟悉了。
4. 简写方式
上面数组的方式,他的分析过程,可以简化一下:
var s1 student = Boy{"zhangsan",}
fmt.Println(s1.StudentName()) // zhagnsan
我们继续分析一下,其实第一种方式的缩写版本,省去了b1
正常写法是这样:
var s1 student
var b1 = Boy{"zhangsan",}
s1 = b1
我们把b1直接省去:
var s1 student
s1 = Boy{"zhangsan",}
连在一起申明并定义:
var s1 student = Boy{"zhangsan",}
就这样,一行代码就实现接口。牛逼!!!
多看多些多练习,就没那么烧脑了!