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",}   

就这样,一行代码就实现接口。牛逼!!!

多看多些多练习,就没那么烧脑了!