Golang之interface
介面型別 是由一組方法簽名定義的集合。
介面型別的變數可以儲存任何實現了這些方法的值。
注意: 示例程式碼的 22 行存在一個錯誤。由於 Abs
方法只為 *Vertex
(指標型別)定義,因此 Vertex
(值型別)並未實現 Abser
。
package main
import (
"fmt"
"math"
)
type Abser interface {
Abs() float64
}
func main() {
var a Abser
f := MyFloat(-math.Sqrt2)
v := Vertex{3, 4}
a = f // a MyFloat 實現了 Abser
a = &v // a *Vertex 實現了 Abser
// 下面一行,v 是一個 Vertex(而不是 *Vertex)
// 所以沒有實現 Abser。
a = v
fmt.Println(a.Abs())
}
type MyFloat float64
func (f MyFloat) Abs() float64 {
if f < 0 {
return float64(-f)
}
return float64(f)
}
type Vertex struct {
X, Y float64
}
func (v *Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
型別通過實現一個介面的所有方法來實現該介面。既然無需專門顯式宣告,也就沒有“implements”關鍵字。
隱式介面從介面的實現中解耦了定義,這樣介面的實現可以出現在任何包中,無需提前準備。
因此,也就無需在每一個實現上增加新的介面名稱,這樣同時也鼓勵了明確的介面定義。
package main
import "fmt"
type I interface {
M()
}
type T struct {
S string
}
// 此方法表示型別 T 實現了介面 I,但我們無需顯式宣告此事。
func (t T) M() {
fmt.Println(t.S)
}
func main() {
var i I = T{"hello"}
i.M()
}
介面也是值。它們可以像其它值一樣傳遞。
介面值可以用作函式的引數或返回值。
在內部,介面值可以看做包含值和具體型別的元組:
(value, type)
介面值儲存了一個具體底層型別的具體值。
介面值呼叫方法時會執行其底層型別的同名方法。
package main
import (
"fmt"
"math"
)
type I interface {
M()
}
type T struct {
S string
}
func (t *T) M() {
fmt.Println(t.S)
}
type F float64
func (f F) M() {
fmt.Println(f)
}
func main() {
var i I
i = &T{"Hello"}
describe(i)
i.M()
i = F(math.Pi)
describe(i)
i.M()
}
func describe(i I) {
fmt.Printf("(%v, %T)\n", i, i)
}
底層值為 nil 的介面值
即便介面內的具體值為 nil,方法仍然會被 nil 接收者呼叫。
在一些語言中,這會觸發一個空指標異常,但在 Go 中通常會寫一些方法來優雅地處理它(如本例中的 M
方法)。
注意: 儲存了 nil 具體值的介面其自身並不為 nil。
package main
import "fmt"
type I interface {
M()
}
type T struct {
S string
}
func (t *T) M() {
if t == nil {
fmt.Println("<nil>")
return
}
fmt.Println(t.S)
}
func main() {
var i I
var t *T
i = t
describe(i)
i.M()
i = &T{"hello"}
describe(i)
i.M()
}
func describe(i I) {
fmt.Printf("(%v, %T)\n", i, i)
}
nil 介面值
nil 介面值既不儲存值也不儲存具體型別。
為 nil 介面呼叫方法會產生執行時錯誤,因為介面的元組內並未包含能夠指明該呼叫哪個 具體 方法的型別。
package main
import "fmt"
type I interface {
M()
}
func main() {
var i I
describe(i)
i.M()
}
func describe(i I) {
fmt.Printf("(%v, %T)\n", i, i)
}
空介面
指定了零個方法的介面值被稱為 *空介面:*
interface{}
空介面可儲存任何型別的值。(因為每個型別都至少實現了零個方法。)
空介面被用來處理未知型別的值。例如,fmt.Print
可接受型別為 interface{}
的任意數量的引數。
package main
import "fmt"
func main() {
var i interface{}
describe(i)
i = 42
describe(i)
i = "hello"
describe(i)
}
func describe(i interface{}) {
fmt.Printf("(%v, %T)\n", i, i)
}
型別斷言 提供了訪問介面值底層具體值的方式。
t := i.(T)
該語句斷言介面值 i
儲存了具體型別 T
,並將其底層型別為 T
的值賦予變數 t
。
若 i
並未儲存 T
型別的值,該語句就會觸發一個恐慌。
為了 判斷 一個介面值是否儲存了一個特定的型別,型別斷言可返回兩個值:其底層值以及一個報告斷言是否成功的布林值。
t, ok := i.(T)
若 i
儲存了一個 T
,那麼 t
將會是其底層值,而 ok
為 true
。
否則,ok
將為 false
而 t
將為 T
型別的零值,程式並不會產生恐慌。
請注意這種語法和讀取一個對映時的相同之處。
package main
import "fmt"
func main() {
var i interface{} = "hello"
s := i.(string)
fmt.Println(s)
s, ok := i.(string)
fmt.Println(s, ok)
f, ok := i.(float64)
fmt.Println(f, ok)
f = i.(float64) // 報錯(panic)
fmt.Println(f)
}
型別選擇
型別選擇 是一種按順序從幾個型別斷言中選擇分支的結構。
型別選擇與一般的 switch 語句相似,不過型別選擇中的 case 為型別(而非值), 它們針對給定介面值所儲存的值的型別進行比較。
switch v := i.(type) { case T: // v 的型別為 T case S: // v 的型別為 S default: // 沒有匹配,v 與 i 的型別相同 }
型別選擇中的宣告與型別斷言 i.(T)
的語法相同,只是具體型別 T
被替換成了關鍵字 type
。
此選擇語句判斷介面值 i
儲存的值型別是 T
還是 S
。在 T
或 S
的情況下,變數 v
會分別按 T
或 S
型別儲存 i
擁有的值。在預設(即沒有匹配)的情況下,變數 v
與 i
的介面型別和值相同。
package main
import "fmt"
func do(i interface{}) {
switch v := i.(type) {
case int:
fmt.Printf("Twice %v is %v\n", v, v*2)
case string:
fmt.Printf("%q is %v bytes long\n", v, len(v))
default:
fmt.Printf("I don't know about type %T!\n", v)
}
}
func main() {
do(21)
do("hello")
do(true)
}
Stringer
type Stringer interface { String() string }
Stringer
是一個可以用字串描述自己的型別。fmt
包(還有很多包)都通過此介面來列印值。
package main
import "fmt"
type Person struct {
Name string
Age int
}
func (p Person) String() string {
return fmt.Sprintf("%v (%v years)", p.Name, p.Age)
}
func main() {
a := Person{"Arthur Dent", 42}
z := Person{"Zaphod Beeblebrox", 9001}
fmt.Println(a, z)
}
錯誤
Go 程式使用 error
值來表示錯誤狀態。
與 fmt.Stringer
類似,error
型別是一個內建介面:
type error interface { Error() string }
(與 fmt.Stringer
類似,fmt
包在列印值時也會滿足 error
。)
通常函式會返回一個 error
值,呼叫的它的程式碼應當判斷這個錯誤是否等於 nil
來進行錯誤處理。
i, err := strconv.Atoi("42") if err != nil { fmt.Printf("couldn't convert number: %v\n", err) return } fmt.Println("Converted integer:", i)
error
為 nil 時表示成功;非 nil 的 error
表示失敗。
package main
import (
"fmt"
"time"
)
type MyError struct {
When time.Time
What string
}
func (e *MyError) Error() string {
return fmt.Sprintf("at %v, %s",
e.When, e.What)
}
func run() error {
return &MyError{
time.Now(),
"it didn't work",
}
}
func main() {
if err := run(); err != nil {
fmt.Println(err)
}
}
Reader
io
包指定了 io.Reader
介面,它表示從資料流的末尾進行讀取。
Go 標準庫包含了該介面的許多實現,包括檔案、網路連線、壓縮和加密等等。
io.Reader
介面有一個 Read
方法:
func (T) Read(b []byte) (n int, err error)
Read
用資料填充給定的位元組切片並返回填充的位元組數和錯誤值。在遇到資料流的結尾時,它會返回一個 io.EOF
錯誤。
示例程式碼建立了一個 strings.Reader
並以每次 8 位元組的速度讀取它的輸出。
package main
import (
"fmt"
"io"
"strings"
)
func main() {
r := strings.NewReader("Hello, Reader!")
b := make([]byte, 8)
for {
n, err := r.Read(b)
fmt.Printf("n = %v err = %v b = %v\n", n, err, b)
fmt.Printf("b[:n] = %q\n", b[:n])
if err == io.EOF {
break
}
}
}
相關文章
- 深入理解Golang之interface和reflectGolang
- Golang高效實踐之interface、reflection、json實踐GolangJSON
- golang使用sqlx報錯:unsupported type []interface {}, a slice of interfaceGolangSQL
- Golang 學習——interface 介面學習(一)Golang
- Golang 學習——interface 介面學習(二)Golang
- 4. 黑科技 Interface |《 刻意學習 Golang 》Golang
- 使用Golang的interface介面設計原則Golang
- Golang語言中的interface是什麼(下)Golang
- Golang語言中的interface是什麼(上)Golang
- Golang中的interface程式碼和允許效果Golang
- golang,interface轉換型別 cannot convert t (typGolang型別
- Golang引入泛型:Go將Interface{}替換為“Any”Golang泛型
- 【Golang】Go 通過結構(struct) 實現介面(interface)GolangStruct
- Golang | Go語言多型的實現與interface使用Golang多型
- Golang | 既是介面又是型別,interface是什麼神仙用法?Golang型別
- 從goim定製, 淺談 golang 的 interface 解耦合與gRPCGolang解耦RPC
- Golang之美Golang
- The ArrayAccess interface
- interface/介面
- golang—踩坑之切片Golang
- Golang通脈之方法Golang
- Golang通脈之反射Golang反射
- Interface中加Static
- public interface View介面和public interface ViewResolver介面介紹View
- Dig101-Go 之 interface 呼叫的一個優化點Go優化
- golang學習之路 之mapGolang
- golang標準庫之 fmtGolang
- Golang每日一庫之regexGolang
- Golang每日一庫之bcryptGolang
- 深入理解 Golang 之 contextGolangContext
- golang 構建工具之 MakefileGolang
- golang基礎之陣列Golang陣列
- golang實戰之flag包Golang
- CHECK_INTERFACE作用
- 如何理解 interface 介面
- Java-介面(interface)Java
- firewalld: 介面interface操作
- 【GoLang 那點事】實踐 gRPC 之 GoLang 入門 HelloWord(三)GolangRPC