在go語言中,接口類型是由一組方法定義的集合。
一個類型是否實現了一個接口,就看這個類型是否實現了接口中定義的所有方法。在go語言中,無需特別的指明?
定義一個接口
type Abser interface {
Abs() float64
}
定義一個結構體
type Vertex struct {
X, Y float64
}
定義兩個方法,一個是結構體指針,一個是結構體。
func (v *Vertex) Abs() float64 {
return v.X * v.X + v.Y * v.Y
}
func (v Vertex) Scale() float64 {
return v.X + v.Y
}
聲明一個接口變量
var a Abser
結構體也實例化一下
f := Vertex{3, 4}
指針也是 Vertex 結構體的指針,所以可以用 f 來實例化。
a = &f
下面你可以分別看一下 a 和 f 都能實現什么方法了。
fmt.Println(f.Abs())
fmt.Println(f.Scale())
fmt.Println(a.Abs())
fmt.Println(a.Scale())
仔細測試,你會發現 fmt.Println(a.Scale()) 是會報錯的“a.Scale undefined (type Abser has no field or method Scale)”。是的,a 沒有 Scale() 這個方法。
為什么呢?
因為 a 是接口 Abser,而接口中沒有定義 Scale()。
如果你加上這個定義 Scale() float64,那么,a.Scale() 就可以實現了。
完整例子
package main
import (
"fmt"
)
type Abser interface {
Abs() float64
}
type Vertex struct {
X, Y float64
}
func (v *Vertex) Abs() float64 {
return v.X * v.X + v.Y * v.Y
}
func (v Vertex) Scale() float64 {
return v.X + v.Y
}
func main() {
var a Abser
f := Vertex{3, 4}
a = &f
fmt.Println(f.Abs())
fmt.Println(f.Scale())
fmt.Println(a.Abs())
//fmt.Println(a.Scale())
}
運行結果
25
7
25
結合上邊的例子,我們可以發現,
類型通過實現方法來實現接口,卻不必要顯示的聲明。所以沒有關鍵字 implements 。這是隱式接口。
隱式接口解耦了實現接口的包和定義接口的包,實現包和定義包“互不依賴”。
Stringers
一個普遍存在的接口,在 fmt 中定義。
type Stringer interface {
String() string
}
我們給它在包內依附一個結構體,定義一個 String() 方法。
type Cofox struct {
name string
}
func (c *Cofox) String() string {
return "Joel " + c.name
}
為了區別原始的值,我們在 Strings() 內的返回值前加了一個字符串 Joel ,以作區別。
看完整代碼
package main
import (
"fmt"
)
type Stringer interface {
String() string
}
type Cofox struct {
name string
}
func (c *Cofox) String() string {
return "Joel " + c.name
}
func main() {
var S Stringer
c := Cofox{"Smith"}
S = &c
fmt.Println(S.String())
fmt.Println(c.String())
fmt.Println(c.name)
}
運行結果如下
Joel Smith
Joel Smith
Smith
接口和結構體都可以使用 String() 函數方法。
你可是試著把 String() 方法里的返回值寫成
return fmt.Sprintf("full name is Joel %v", c.name)
運行自己看看結果有無不同。
再寫一個例子,這次結構體多加一個字段,看看如何應用。
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{"Joel", 31}
z := Person{"Smith", 45}
fmt.Println(a, z)
}
運行結果
Joel (31 years) Smith (45 years)