【Golang】快速复习指南QuickReview(七)——interface
在C#中,接口是实现多态的方式之一,但是接口更侧重对象的能力,是一种规范。如果继承了接口,就必须按照接口的要求去实现这个接口。接口与接口之间是可以有继承。而golang
中的接口,是一组方法的集合体,duck-type programming
的一种体现。
如果有一种动物能够想鸭子那样行走,叫的也像鸭子,那么我们认为这就是鸭子。
1.C#的接口
前文提到,C#的接口侧重于能力,好的接口功能(能力)单一,接口能继承接口,类能继承多个接口(多种能力),如果继承了接口,就必须全部实现。
1.1 接口定义
接口中可以定义属性,索引器,甚至事件,接下来我们定义一个运动员IPlayer
的接口:
public interface IPlayer
{
//接口可以定义属性
string Name { get; set; }
double Height { get; set; }
int Age { get; set; }
//运动
void PlaySport();
}
1.2 接口继承接口
再定义一个职业篮球运动员接口IBasketPlayer
,
- 首先职业篮球运动员也是一个运动员,所以继承运动员接口
IPlayer
- 其次,不管是哪个联赛的职业运动员,除了有一些通用的技术能力,都会面临转会事件
TransferEvent
public interface IBasketPlayer: IPlayer
{
//臂展
double Wingspan { get; }
//垂直摸高
double Verticalreach { get; }
//垂直跳跃
double Verticalleap { get; }
//索引器 为了演示,强行加一个索引器
string this[string index]
{
get; set;
}
event EventHandler TransferEvent;
//扣篮
void Dunk();
//传球
void Pass();
void Dribble();
//3分球
void ThreePointShot();
//中远距离
void TwoPointShot();
//空接
void AlleyOop();
//篮板
void Backboard();
}
1.3 实现接口
- 定义一个NBA球员
NBAPlayer
public class NBAPlayer : IBasketPlayer
{
//索引器
public string this[string index] { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
//臂展
public double Wingspan => throw new NotImplementedException();
//垂直摸高
public double Verticalreach => throw new NotImplementedException();
//垂直起跳
public double Verticalleap => throw new NotImplementedException();
//姓名
public string Name { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
//身高
public double Height { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
//年龄
public int Age { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
public event EventHandler TransferEvent;
public void AlleyOop()
{
throw new NotImplementedException();
}
public void Backboard()
{
throw new NotImplementedException();
}
public void Dribble()
{
throw new NotImplementedException();
}
public void Dunk()
{
throw new NotImplementedException();
}
public void Pass()
{
throw new NotImplementedException();
}
public void PlaySport()
{
throw new NotImplementedException();
}
public void ThreePointShot()
{
throw new NotImplementedException();
}
public void TwoPointShot()
{
throw new NotImplementedException();
}
}
- 在定义一个CBA球员 实现接口
CBAPlayer
:
public class CBAPlayer : IBasketPlayer
{
//索引器
public string this[string index] { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
//臂展
public double Wingspan => throw new NotImplementedException();
//锤子摸高
public double Verticalreach => throw new NotImplementedException();
//垂直起跳
public double Verticalleap => throw new NotImplementedException();
//姓名
public string Name { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
//身高
public double Height { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
//年龄
public int Age { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
//转会事件
public event EventHandler TransferEvent;
public void AlleyOop()
{
throw new NotImplementedException();
}
public void Backboard()
{
throw new NotImplementedException();
}
public void Dribbl()
{
throw new NotImplementedException();
}
public void Dunk()
{
throw new NotImplementedException();
}
public void Pass()
{
throw new NotImplementedException();
}
public void PlaySport()
{
throw new NotImplementedException();
}
public void ThreePointShot()
{
throw new NotImplementedException();
}
public void TwoPointShot()
{
throw new NotImplementedException();
}
}
1.4 实现多个接口
NBA很多黑人球员,不但会打篮球,还会说唱,(黑人,人均会跳舞,会说唱,^_^),比较有名的有艾弗森,阿泰斯特,利拉德,尤其是艾弗森Allen Iverson,很多音乐平台都能搜到,但是CBA球员就不一定会说唱,所以继续定义一个说唱接口 IRapper
public interface IRapper
{
void Rapper();
}
public class NBAPlayer : IBasketPlayer,IRapper
{
//索引器
public string this[string index] { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
//臂展
public double Wingspan => throw new NotImplementedException();
//锤子摸高
public double Verticalreach => throw new NotImplementedException();
//垂直起跳
public double Verticalleap => throw new NotImplementedException();
//姓名
public string Name { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
//身高
public double Height { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
//年龄
public int Age { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
//转会
public event EventHandler TransferEvent;
//会说唱
public void Rapper()
{
throw new NotImplementedException();
}
public void AlleyOop()
{
throw new NotImplementedException();
}
public void Backboard()
{
throw new NotImplementedException();
}
public void Dribbl()
{
throw new NotImplementedException();
}
public void Dunk()
{
throw new NotImplementedException();
}
public void Pass()
{
throw new NotImplementedException();
}
public void PlaySport()
{
throw new NotImplementedException();
}
public void ThreePointShot()
{
throw new NotImplementedException();
}
public void TwoPointShot()
{
throw new NotImplementedException();
}
}
2.Golang的接口
C#的接口可以说一种规范,它可以包含数据的规范,比如属性,事件,索引器,也可以包含行为(方法)的规范,但是Golang
有所不同:Golang
不关心数据,只关心行为(方法)。
2.1 接口定义
接口是一种类型,抽象类型,就像定义struct
一样定义:
type Player interface {
PlaySport()
}
2.2 实现接口
一个对象(能作为方法的接收者)只要实现了接口中定义的方法,那么就算实现了这个接口。
func main(){
var player Player = &NBAPlayer{
Name: "James",
}
player.PlaySport()
}
type Player interface {
PlaySport()
}
type NBAPlayer struct {
Name string
Height float32
Age int8
Wingspan float32
Verticalreach float32
Verticalleap float32
}
//指针接收者实现接口
func (p *NBAPlayer) PlaySport() {
fmt.Println(p.Name, "从事的运动项目是打篮球")
}
James 从事的运动项目是打篮球
ps:如果是上面的代码采用值接收者
func (p NBAPlayer) PlaySport()
,无论是结构体还是结构体指针都可以赋值给接口变量,因为Go语言中有对指针类型变量求值的语法糖,结构体指针内部会自动求值*struct
。如果是上面代码那样采用指针接收者,那么接口变量就必须传指针。这个问题如果不熟练,会在实际编码中一次次被编译器打脸。
2.3 实现多个接口
一个NBA球员既要实现Player
接口,又要实现Rapper
接口:
func main(){
var player Player = &NBAPlayer{
Name: "James",
}
var rapper Rapper = &NBAPlayer{
Name: "James",
}
player.PlaySport()
rapper.Rapper()
}
type Rapper interface {
Rapper()
}
type Player interface {
PlaySport()
}
type NBAPlayer struct {
Name string
Height float32
Age int8
Wingspan float32
Verticalreach float32
Verticalleap float32
}
//指针接收者实现接口
func (p *NBAPlayer) PlaySport() {
fmt.Println(p.Name, "从事的运动项目是打篮球")
}
func (p *NBAPlayer) Rapper() {
fmt.Println(p.Name, "还会说唱")
}
James 从事的运动项目是打篮球
James 还会说唱
2.4 嵌套接口
接口BasketPlayer
嵌套接口Player
,Rapper
,这个就类似于C#接口可以继承接口。
func main(){
var nbaplayer BasketPlayer = &NBAPlayer{
Name: "Allen Iverson",
}
nbaer.PlaySport()
nbaer.Rapper()
nbaer.Dunk()
}
type BasketPlayer interface {
Player
Rapper
Dunk()
}
func (p *NBAPlayer) PlaySport() {
fmt.Println(p.Name, "从事的运动项目是打篮球")
}
func (p *NBAPlayer) Rapper() {
fmt.Println(p.Name, "还会说唱")
}
func (p *NBAPlayer) Dunk() {
fmt.Println(p.Name, "会扣篮")
}
Allen Iverson 从事的运动项目是打篮球
Allen Iverson 还会说唱
Allen Iverson 会扣篮
2.5* 空接口
空接口是指没有定义任何方法的接口。**因此任何类型都实现了空接口。**套用周星驰电影的台词,”其实根本没有什么空接口,或许一切都是都是空接口“,空接口类型的变量可以存储任意类型的变量。
2.5.1 空接口切片
- 看一下
fmt
包的Println
方法,参数就是一个空接口的切片
func Println(a ...interface{}) (n int, err error) {
return Fprintln(os.Stdout, a...)
}
2.5.2 保存任意值的map
- 我们定义map
make(map[TKey]TValue)
,当TValue
换成interface{}
空接口时,这时候的map
的value
就不再是单一的类型,而是可以为任意类型。
var player = make(map[string]interface{})
player["name"] = "LeBron James"
player["age"] = 36
2.5.3 类型断言
类型断言主要用于判断空接口中的值,因为空接代表任意类型。x.(T)
var x interface{}
x = "randyfield"
v, ok := x.(string)
if ok {
fmt.Println(v)
} else {
fmt.Println("断言失败")
}
关于接口,不要为了接口而写接口,会增加不必要的抽象,导致不必要的运行时损耗。
再次强调:这个系列并不是教程,如果想系统的学习,博主可推荐学习资源。
- 原文作者:Garfield
- 原文链接:http://www.randyfield.cn/post/2020-11-27-golang-interface/
- 版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可,非商业转载请注明出处(作者,原文链接),商业转载请联系作者获得授权。