t {
X, Y float64
}
func (v Vertex) Abs() float64 {
reutrn math.Sqrt(v.X * v.X + v.Y * v.Y)
}
func AbsFunc(v Vertex) float64 {
return math.Sqrt(v.X * v.X + v.Y * v.Y)
}
func main() {
v := Vertex{3, 4}
fmt.Println(v.Abs()) // 5
fmt.Println(AbsFunc(v)) // 5
p := &Vertex{4, 3}
fmt.Println(p.Abs()) // 5
fmt.Println(AbsFunc(*p)) // 5
}
2.接口
type I interface {
[funcs]()
}
接口类型interface
是由一组方法签名定义的集合。
接口类型的变量可以保存任何实现了这些方法的值。
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() // Result: Hello
}
接口也是值。
接口可以像其它值一样传递。
接口值也可以用作函数的参数或返回值。
在内部,接口值可以看做包含值和具体类型的元组:
(value, type)
接口值保存了一个具体底层类型的具体值。
接口值调用方法时会执行其底层类型的同名方法。
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 describe(i I) {
fmt.Printf("(%v, %T)\n", i, i)
}
func main() {
var i I
i = &T{"Hello"}
describe(i) // (&{Hello}, *main.T)
i.M() // Hello
i = F(math.Pi)
describe(i) // (3.141592653589793, main.F)
i.M() // 3.141592653589793
}
底层值为nil
的接口值。
即便接口内的具体值为nil
,方法仍然会被nil
接收者调用。
保存了nil
具体值的接口其自身并不为nil
。
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 describe(i I) {
fmt.Printf("(%v, %T)\n", i, i)
}
func main() {
var i I
// nil接口值既不保存值也不保存具体类型。
// 为nil接口调用方法会产生运行时错误,因为接口的元组内并未包含能够指明该调用哪个具体方法的类型。
describe(i) // (<nil>, <nil>)
i.M() // 编译错误!
var t *T
i = t
describe(i) // (<nil>, *main.T)
i.M() // <nil>
i = &T{"Hello"}
describe(i) // (&{hello}, *main.T)
i.M() // hello
}
指定了零个方法的接口值被称为空接口。
interface {}
空接口可以保存任何类型的值(因为每个类型都至少实现了零个方法)。
空接口被用来处理未知类型的值。
例如fmt.Print
可接受类型为interface {}
的任意数量的参数。
3.类型断言
类型断言提供了访问接口值底层具体值的方式。
t := i.(T)
为了判断一个接口值是否保存了一个特定的类型,类型断言可返回两个值:其底层值和一个报告断言是否成功的布尔值。
t, ok := i.(T)
如果i
保存了一个T
,那么t
将会是其底层值,而ok
为true
;否则ok
将为false
,而t
为类型T
的零值。
4.类型选择
类型选择是一种按顺序从几个类型断言中选择分支的结构。
与switch
语句相似,case
为类型。
switch v := i.(type) {
case T:
// v的类型为T
case S:
// v的类型为S
default:
// 没有匹配 v与i的类型相同
}
5.Stringer
fmt
包中定义的Stringer
是最普遍的接口之一。
fmt.Sprintf()
package main
import "fmt"
type IPAddr [4]byte
// TODO: 给 IPAddr 添加一个 "String() string" 方法
func (ip IPAddr) String() string {
return fmt.Sprintf("%v.%v.%v.%v", ip[0], ip[1], ip[2], ip[3])
}
func main() {
hosts := map[string]IPAddr{
"loopback": {127, 0, 0, 1},
"googleDNS": {8, 8, 8, 8},
}
for name, ip := range hosts {
fmt.Printf("%v: %v\n", name, ip)
}
}
6.错误
Go程序使用error
值来表示错误状态。
error
类型是一个内建接口:
type error interface {
Error() string
}
error
为nil
时表示成功,否则是失败。
7. Reader
io
包指定了io.Reader
接口,它表示从数据流的末尾进行读取。
在遇到数据流的结尾时,它会返回一个io.EOF
错误。
func (T) Read(b []byte) (n int, err error)
8. 图像
image
包定义了Image
接口。
package image
type Image interface {
ColorModel() color.Model
Bounds() Rectangle
At(x, y int) color.Color
}
package main
import (
"golang.org/x/tour/pic"
"image"
"image/color"
)
type Image struct{}
func (i Image) ColorModel() color.Model {
return color.RGBAModel
}
func (i Image) Bounds() image.Rectangle {
return image.Rect(0