方法Methods
go没有类(classes),但可以在类型(types)上定义方法。receiver定义在func关键字和方法名之间。如下例:Abs方法有一个Vertex类型的名为v的receiver。receiver的作用相当于在Vertex类型新增了一个方法Abs()
package main
import (
"fmt"
"math"
)
type Vertex struct {
X, Y float64
}
func (v Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
func main() {
v := Vertex{3, 4}
fmt.Println(v.Abs())
}
方法不仅可以定义在结构体类型,也可以定义在非结构体(non-struct)类型上
方法依赖的类型必须在本包内已经定义,在其他包或内建包上的类型则不行
其他包import进来用type类型声明一下也可以定义方法
package main
import (
"fmt"
"math"
)
type MyFloat float64
func (f MyFloat) Abs() float64 {
if f < 0 {
return float64(-f)
}
return float64(f)
}
func main() {
f := MyFloat(-math.Sqrt2)
fmt.Println(f.Abs())
}
// 1.4142135623730951
指针接收器(Pointer receivers)
receiver可以是指针类型,指针类型也可以定义方法
带有pointer receiver的方法可以修改receiver所指向的值,它比value receiver更普遍。换句话说,value receiver操作的是receiver类型的变量的拷贝,而pointer receiver改变的是receiver类型的变量本身。
package main
import (
"fmt"
"math"
)
type Vertex struct {
X, Y float64
}
func (v Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
func (v *Vertex) Scale(f float64) {
v.X = v.X * f
v.Y = v.Y * f
}
func main() {
v := Vertex{3, 4}
v.Scale(10)
fmt.Println(v.Abs())
}
// 50
方法和函数区别
函数如果传的是地址,必须用指针来接受,而方法可以传值或地址给pointer receiver
如:
// 函数
var v Vertex
ScaleFunc(v, 5) // Compile error!
ScaleFunc(&v, 5) // OK
// 方法
var v Vertex
v.Scale(5) // OK
p := &v
p.Scale(10) // OK
package main
import "fmt"
type Vertex struct {
X, Y float64
}
func (v *Vertex) Scale(f float64) {
v.X = v.X * f
v.Y = v.Y * f
}
func ScaleFunc(v *Vertex, f float64) {
v.X = v.X * f
v.Y = v.Y * f
}
func main() {
// 传值给pointer receiver
v := Vertex{3, 4}
v.Scale(2)
ScaleFunc(&v, 10)
// 传地址给pointer receiver
p := &Vertex{4, 3}
p.Scale(3)
ScaleFunc(p, 8)
fmt.Println(v, p)
// {60 80} &{96 72}
}
函数传递地址的变量必须已经赋值,如:
var v Vertex
fmt.Println(AbsFunc(v)) // OK
fmt.Println(AbsFunc(&v)) // Compile error!
方法则没有以上限制,如:
var v Vertex
fmt.Println(v.Abs()) // OK
p := &v
fmt.Println(p.Abs()) // OK
选择value receiver还是pointer receiver
通常需要改变receiver指向的值或者不希望出现多份值的拷贝时用pointer receiver,其余可用value pointer,但两者不能同时出现
package main
import (
"fmt"
"math"
)
type Vertex struct {
X, Y float64
}
func (v *Vertex) Scale(f float64) {
v.X = v.X * f
v.Y = v.Y * f
}
func (v *Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
func main() {
v := &Vertex{3, 4}
fmt.Printf("Before scaling: %+v, Abs: %v\n", v, v.Abs())
v.Scale(5)
fmt.Printf("After scaling: %+v, Abs: %v\n", v, v.Abs())
}
// Before scaling: &{X:3 Y:4}, Abs: 5
// After scaling: &{X:15 Y:20}, Abs: 25
接口(Interfaces)
接口是方法签名的集合
定义接口:
type 接口名 interface
接口名通常以er结尾
package main
import (
"fmt"
"math"
)
// 定义接口Abser
type Abser interface {
Abs() float64
}
func main() {
var a Abser
f := MyFloat(-math.Sqrt2)
v := Vertex{3, 4}
a = f // a MyFloat implements Abser
a = &v // a *Vertex implements Abser
// 下例中v是Vertex (不是 *Vertex)
// 没有实现 Abser.
a = v
fmt.Println(a.Abs())
}
type MyFloat float64
// MyFloat实现接口
func (f MyFloat) Abs() float64 {
if f < 0 {
return float64(-f)
}
return float64(f)
}
type Vertex struct {
X, Y float64
}
// Vertex实现接口
func (v *Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
// ./prog.go:22:4: cannot use v (type Vertex) as type Abser in assignment:
// Vertex does not implement Abser (Abs method has pointer receiver)
一个类型实现了接口的所有方法就称实现了该接口,无需显示声明
package main
import "fmt"
type I interface {
M()
}
type T struct {
S string
}
// This method means type T implements the interface I,
// but we don't need to explicitly declare that it does so.
func (t T) M() {
fmt.Println(t.S)
}
func main() {
var i I = T{"hello"}
i.M()
}
// hello
接口值(Interface values)
在底层,接口类型的值被当做值和类型的元组:
(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)
}
// (&{Hello}, *main.T)
// Hello
// (3.141592653589793, main.F)
// 3.141592653589793
未赋值变量可以调用接口的方法,此时receiver是nil receiver
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 为赋值,依然调用了接口中的方法
i.M()
i = &T{"hello"}
describe(i)
i.M()
}
func describe(i I) {
fmt.Printf("(%v, %T)\n", i, i)
}
没有实现的接口中的方法不能调用
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)
}
// panic: runtime error: invalid memory address or nil pointer dereference
空接口(empty interface)
空接口: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)
}
// (<nil>, <nil>)
// (42, int)
// (hello, string)
类型断言(Type assertions)
类型断言允许在隐含条件下访问接口中的值
t := i.(T),这条语句假设接口变量i拥有类型T的实例,并把它的值赋给t,如果没有将引发panic。可以用类型断言解决这个问题:
t, ok := i.(T)
如果i有实例T,则把它的值赋给t,ok为true;否则t为zero值,ok为false
类型断言必须加()
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)
}
// hello
// hello true
// 0 false
// panic: interface conversion: interface {} is string, not float64
i.(type)可以得到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)
}
// Twice 21 is 42
// "hello" is 5 bytes long
// I don't know about type bool!
Stringer
Stringer是定义在fmt中最常见的接口,实现这个接口fmt就知道如何把这个类型转化成string
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)
}
// Arthur Dent (42 years) Zaphod Beeblebrox (9001 years)
错误(Errors)
error是一个内建的接口。大部分函数返回一个error值,由调用它的函数判断是否等于nil,等于nil表示运行成功。自定义error需实现Error()接口。如:
i, err := strconv.Atoi("42")
if err != nil {
fmt.Printf("couldn't convert number: %v\n", err)
return
}
fmt.Println("Converted integer:", i)
package main
import (
"fmt"
"time"
)
type MyError struct {
When time.Time
What string
}
// MyError实现了Error()接口
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)
}
}
// at 2009-11-10 23:00:00 +0000 UTC m=+0.000000001, it didn't work
读(Reader)
io包定义了io.Reader接口,go标准库有许多实现了该接口 many implementations。该接口有一个Read方法:
func (T) Read(b []byte) (n int, err error)
方法接收比特类型切片,返回读取比特数和error,读到末尾是error为io.EOF
package main
import (
"fmt"
"io"
"strings"
)
func main() {
r := strings.NewReader("Hello, Reader!")
b := make([]byte, 8)
for {
// 读到的数据存到b数组,n为读取比特个数
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
}
}
}
// n = 8 err = <nil> b = [72 101 108 108 111 44 32 82]
// b[:n] = "Hello, R"
// n = 6 err = <nil> b = [101 97 100 101 114 33 32 82]
// b[:n] = "eader!"
// n = 0 err = EOF b = [101 97 100 101 114 33 32 82]
// b[:n] = ""
图像(Images)
image包定义了图像接口
package image
type Image interface {
ColorModel() color.Model
Bounds() Rectangle
At(x, y int) color.Color
}
func NewNRGBA(r Rectangle) *RGBA返回指定边界的图像
package main
import (
"fmt"
"image"
)
func main() {
m := image.NewRGBA(image.Rect(0, 0, 100, 100))
fmt.Println(m.Bounds())
fmt.Println(m.At(0, 0).RGBA())
}
// (0,0)-(100,100)
// 0 0 0 0