在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("斷言失敗")
}
關於接口,不要爲了接口而寫接口,會增加不必要的抽象,導致不必要的運行時損耗。