[TOC]
## 四、interface
### (1) interface的賦值問題
> 以下代碼能編譯過去嗎?為什么?
> test12.go
```go
package main
import (
"fmt"
)
type People interface {
Speak(string) string
}
type Stduent struct{}
func (stu *Stduent) Speak(think string) (talk string) {
if think == "love" {
talk = "You are a good boy"
} else {
talk = "hi"
}
return
}
func main() {
var peo People = Stduent{}
think := "love"
fmt.Println(peo.Speak(think))
}
```
繼承與多態的特點
在golang中對多態的特點體現從語法上并不是很明顯。
我們知道發生多態的幾個要素:
1、有interface接口,并且有接口定義的方法。
2、有子類去重寫interface的接口。
3、有父類指針指向子類的具體對象
那么,滿足上述3個條件,就可以產生多態效果,就是,父類指針可以調用子類的具體方法。
所以上述代碼報錯的地方在`var peo People = Stduent{}`這條語句, `Student{}`已經重寫了父類`People{}`中的`Speak(string) string`方法,那么只需要用父類指針指向子類對象即可。
所以應該改成`var peo People = &Student{}` 即可編譯通過。(People為interface類型,就是指針類型)
### (2) interface的內部構造(非空接口iface情況)
> 以下代碼打印出來什么內容,說出為什么。
> test14.go
```go
package main
import (
"fmt"
)
type People interface {
Show()
}
type Student struct{}
func (stu *Student) Show() {
}
func live() People {
var stu *Student
return stu
}
func main() {
if live() == nil {
fmt.Println("AAAAAAA")
} else {
fmt.Println("BBBBBBB")
}
}
```
**結果**
```bash
BBBBBBB
```
**分析:**
我們需要了解`interface`的內部結構,才能理解這個題目的含義。
interface在使用的過程中,共有兩種表現形式
一種為**空接口(empty interface)**,定義如下:
```go
var MyInterface interface{}
```
另一種為**非空接口(non-empty interface)**, 定義如下:
```go
type MyInterface interface {
function()
}
```
這兩種interface類型分別用兩種`struct`表示,空接口為`eface`, 非空接口為`iface`.

---
#### **空接口eface**
空接口eface結構,由兩個屬性構成,一個是類型信息_type,一個是數據信息。其數據結構聲明如下:
```go
type eface struct { //空接口
_type *_type //類型信息
data unsafe.Pointer //指向數據的指針(go語言中特殊的指針類型unsafe.Pointer類似于c語言中的void*)
}
```
**_type屬性**:是GO語言中所有類型的公共描述,Go語言幾乎所有的數據結構都可以抽象成 _type,是所有類型的公共描述,**type負責決定data應該如何解釋和操作,**type的結構代碼如下:
```go
type _type struct {
size uintptr //類型大小
ptrdata uintptr //前綴持有所有指針的內存大小
hash uint32 //數據hash值
tflag tflag
align uint8 //對齊
fieldalign uint8 //嵌入結構體時的對齊
kind uint8 //kind 有些枚舉值kind等于0是無效的
alg *typeAlg //函數指針數組,類型實現的所有方法
gcdata *byte
str nameOff
ptrToThis typeOff
}
```
**data屬性:** 表示指向具體的實例數據的指針,他是一個`unsafe.Pointer`類型,相當于一個C的萬能指針`void*`。

---
#### 非空接口iface
iface 表示 non-empty interface 的數據結構,非空接口初始化的過程就是初始化一個iface類型的結構,其中`data`的作用同`eface`的相同,這里不再多加描述。
```go
type iface struct {
tab *itab
data unsafe.Pointer
}
```
iface結構中最重要的是itab結構(結構如下),每一個 `itab` 都占 32 字節的空間。itab可以理解為`pair<interface type, concrete type>` 。itab里面包含了interface的一些關鍵信息,比如method的具體實現。
```go
type itab struct {
inter *interfacetype // 接口自身的元信息
_type *_type // 具體類型的元信息
link *itab
bad int32
hash int32 // _type里也有一個同樣的hash,此處多放一個是為了方便運行接口斷言
fun [1]uintptr // 函數指針,指向具體類型所實現的方法
}
```
其中值得注意的字段,個人理解如下:
1. `interface type`包含了一些關于interface本身的信息,比如`package path`,包含的`method`。這里的interfacetype是定義interface的一種抽象表示。
2. `type`表示具體化的類型,與eface的 *type類型相同。*
3. `hash`字段其實是對`_type.hash`的拷貝,它會在interface的實例化時,用于快速判斷目標類型和接口中的類型是否一致。另,Go的interface的Duck-typing機制也是依賴這個字段來實現。
4. `fun`字段其實是一個動態大小的數組,雖然聲明時是固定大小為1,但在使用時會直接通過fun指針獲取其中的數據,并且不會檢查數組的邊界,所以該數組中保存的元素數量是不確定的。

---
所以,People擁有一個Show方法的,屬于非空接口,People的內部定義應該是一個`iface`結構體
```go
type People interface {
Show()
}
```

```go
func live() People {
var stu *Student
return stu
}
```
stu是一個指向nil的空指針,但是最后`return stu` 會觸發`匿名變量 People = stu`值拷貝動作,所以最后`live()`放回給上層的是一個`People insterface{}`類型,也就是一個`iface struct{}`類型。 stu為nil,只是`iface`中的data 為nil而已。 但是`iface struct{}`本身并不為nil.

所以如下判斷的結果為`BBBBBBB`:
```go
func main() {
if live() == nil {
fmt.Println("AAAAAAA")
} else {
fmt.Println("BBBBBBB")
}
}
```
### (3) interface內部構造(空接口eface情況)
> 下面代碼結果為什么?
```go
func Foo(x interface{}) {
if x == nil {
fmt.Println("empty interface")
return
}
fmt.Println("non-empty interface")
}
func main() {
var p *int = nil
Foo(p)
}
```
**結果**
```bash
non-empty interface
```
**分析**
不難看出,`Foo()`的形參`x interface{}`是一個空接口類型`eface struct{}`。

在執行`Foo(p)`的時候,觸發`x interface{} = p`語句,所以此時 x結構如下。

所以 x 結構體本身不為nil,而是data指針指向的p為nil。
---
### (4) inteface{}與*interface{}
> ABCD中哪一行存在錯誤?
> test15.go
```go
type S struct {
}
func f(x interface{}) {
}
func g(x *interface{}) {
}
func main() {
s := S{}
p := &s
f(s) //A
g(s) //B
f(p) //C
g(p) //D
}
```
**結果**
```bash
B、D兩行錯誤
B錯誤為: cannot use s (type S) as type *interface {} in argument to g:
*interface {} is pointer to interface, not interface
D錯誤為:cannot use p (type *S) as type *interface {} in argument to g:
*interface {} is pointer to interface, not interface
```
看到這道題需要第一時間想到的是Golang是強類型語言,interface是所有golang類型的父類 函數中`func f(x interface{})`的`interface{}`可以支持傳入golang的任何類型,包括指針,但是函數`func g(x *interface{})`只能接受`*interface{}`
- 封面
- 第一篇:Golang修養必經之路
- 1、最常用的調試 golang 的 bug 以及性能問題的實踐方法?
- 2、Golang的協程調度器原理及GMP設計思想?
- 3、Golang中逃逸現象, 變量“何時棧?何時堆?”
- 4、Golang中make與new有何區別?
- 5、Golang三色標記+混合寫屏障GC模式全分析
- 6、面向對象的編程思維理解interface
- 7、Golang中的Defer必掌握的7知識點
- 8、精通Golang項目依賴Go modules
- 9、一站式精通Golang內存管理
- 第二篇:Golang面試之路
- 1、數據定義
- 2、數組和切片
- 3、Map
- 4、interface
- 5、channel
- 6、WaitGroup
- 第三篇、Golang編程設計與通用之路
- 1、流?I/O操作?阻塞?epoll?
- 2、分布式從ACID、CAP、BASE的理論推進
- 3、對于操作系統而言進程、線程以及Goroutine協程的區別
- 4、Go是否可以無限go? 如何限定數量?
- 5、單點Server的N種并發模型匯總
- 6、TCP中TIME_WAIT狀態意義詳解
- 7、動態保活Worker工作池設計