概述
Go 中檢測(cè)一個(gè)類(lèi)型是否實(shí)現(xiàn)了某個(gè)接口,通常分為兩類(lèi)機(jī)制: 編譯期間
和 運(yùn)行期間
。
編譯期間
顧名思義,編譯期間檢測(cè)就是通過(guò)靜態(tài)分析來(lái)確定類(lèi)型是否實(shí)現(xiàn)了某個(gè)接口,如果檢測(cè)不通過(guò),則編譯過(guò)程報(bào)錯(cuò)并退出。通常將這種方式稱(chēng)為 接口完整性檢查
。
接口完整性檢查
類(lèi)型未實(shí)現(xiàn)接口
package main
type Person interface {
Name() string
Age() int
}
type Martian struct {
}
func main() {
// 強(qiáng)制類(lèi)型 Martian 必須實(shí)現(xiàn)接口 Person 的所有方法
var _ Person = (*Martian)(nil)
// 1. 聲明一個(gè) _ 變量 (不使用)
// 2. 把一個(gè) nil 轉(zhuǎn)換為 (*Martian),然后再轉(zhuǎn)換為 Person
// 3. 如果 Martian 沒(méi)有實(shí)現(xiàn) Person 的全部方法,則轉(zhuǎn)換失敗,編譯器報(bào)錯(cuò)
}
運(yùn)行代碼
$ go run main.go
# 輸出如下
cannot use (*Martian)(nil) (value of type *Martian) as type Person in variable declaration:
*Martian does not implement Person (missing Age method)
從輸出的結(jié)果中可以看到,Martian
并沒(méi)有實(shí)現(xiàn) Person
接口,所以報(bào)錯(cuò)了。下面我們?yōu)?Martian
實(shí)現(xiàn) Person
接口。
類(lèi)型實(shí)現(xiàn)了接口
package main
import "fmt"
type Person interface {
Name() string
Age() int
}
type Martian struct {
}
// 實(shí)現(xiàn) Name 方法
func (m *Martian) Name() string {
return "martian"
}
// 實(shí)現(xiàn) Age 方法
func (m *Martian) Age() int {
return -1
}
func main() {
// 此時(shí) Martian 已實(shí)現(xiàn)了 Person 的全部方法
var _ Person = (*Martian)(nil)
m := &Martian{}
fmt.Printf("name = %s, age = %d\\n", m.Name(), m.Age())
}
運(yùn)行代碼
$ go run main.go
# 輸出如下
name = martian, age = -1
從輸出的結(jié)果中可以看到,運(yùn)行成功,Martian
已經(jīng)實(shí)現(xiàn)了 Person
接口的全部方法。
運(yùn)行期間
運(yùn)行期間的檢測(cè)方式主要有 類(lèi)型斷言
和 反射
。
類(lèi)型斷言
類(lèi)型未實(shí)現(xiàn)接口
package main
import "fmt"
type Person interface {
Name() string
Age() int
}
type Martian struct {
}
func main() {
// 變量必須聲明為 interface 類(lèi)型
var m interface{}
m = &Martian{}
if v, ok := m.(Person); ok {
fmt.Printf("name = %s, age = %d\\n", v.Name(), v.Age())
} else {
fmt.Println("Martian does not implements Person")
}
}
運(yùn)行代碼
$ go run main.go
# 輸出如下
Martian does not implements Person
下面我們?yōu)?Martian
實(shí)現(xiàn) Person
接口。
類(lèi)型實(shí)現(xiàn)了接口
package main
import "fmt"
type Person interface {
Name() string
Age() int
}
type Martian struct {
}
func (m *Martian) Name() string {
return "martian"
}
func (m *Martian) Age() int {
return -1
}
func main() {
// 變量必須聲明為 interface 類(lèi)型
var m interface{}
m = &Martian{}
if v, ok := m.(Person); ok {
fmt.Printf("name = %s, age = %d\\n", v.Name(), v.Age())
}
}
運(yùn)行代碼
$ go run main.go
# 輸出如下
name = martian, age = -1
從輸出的結(jié)果中可以看到,運(yùn)行成功,Martian
已經(jīng)實(shí)現(xiàn)了 Person
接口的全部方法。
反射
通過(guò) reflect
包提供的 API 來(lái)判斷類(lèi)型是否實(shí)現(xiàn)了某個(gè)接口。
類(lèi)型未實(shí)現(xiàn)接口
package main
import (
"fmt"
"reflect"
)
type Person interface {
Name() string
Age() int
}
type Martian struct {
}
func main() {
// 獲取 Person 類(lèi)型
p := reflect.TypeOf((*Person)(nil)).Elem()
// 獲取 Martian 結(jié)構(gòu)體指針類(lèi)型
m := reflect.TypeOf(&Martian{})
// 判斷 Martian 結(jié)構(gòu)體類(lèi)型是否實(shí)現(xiàn)了 Person 接口
fmt.Println(m.Implements(p))
}
運(yùn)行代碼
$ go run main.go
# 輸出如下
false
下面我們?yōu)?Martian
實(shí)現(xiàn) Person
接口。
類(lèi)型實(shí)現(xiàn)了接口
package main
import (
"fmt"
"reflect"
)
type Person interface {
Name() string
Age() int
}
type Martian struct {
}
func (m *Martian) Name() string {
return "martian"
}
func (m *Martian) Age() int {
return -1
}
func main() {
// 獲取 Person 類(lèi)型
p := reflect.TypeOf((*Person)(nil)).Elem()
// 獲取 Martian 結(jié)構(gòu)體指針類(lèi)型
m := reflect.TypeOf(&Martian{})
// 判斷 Martian 結(jié)構(gòu)體類(lèi)型是否實(shí)現(xiàn)了 Person 接口
fmt.Println(m.Implements(p))
}
運(yùn)行代碼
$ go run main.go
# 輸出如下
true
小結(jié)
Go 的接口實(shí)現(xiàn)檢測(cè)機(jī)制分為 編譯期間
和 運(yùn)行期間
,其中編譯期間的檢測(cè)方式是 接口完整性檢查
, 而運(yùn)行期間的檢測(cè)方式有兩種: 類(lèi)型斷言
和 反射
,一般情況盡量使用類(lèi)型斷言,這樣可以避免反射帶來(lái)的性能損耗。文中提到的幾種檢測(cè)方式的代碼語(yǔ)法都比較新 (nan) 奇 (kan) ,讀者可以參考代碼的注釋部分幫助理解。
-
VaR
+關(guān)注
關(guān)注
0文章
39瀏覽量
11504 -
API接口
+關(guān)注
關(guān)注
1文章
85瀏覽量
10731 -
go語(yǔ)言
+關(guān)注
關(guān)注
1文章
158瀏覽量
9243
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
HarmonyOS Next原生應(yīng)用開(kāi)發(fā)-從TS到ArkTS的適配規(guī)則(十)

評(píng)論