-
Notifications
You must be signed in to change notification settings - Fork 0
Description
-
Methods: 有
receiver的functiongo没有class的概念,可以通过给数据类型扩展Methods的方式实现类似class的功能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()) }任何数据类型都可以扩展Methods, 在同一个
package可用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()) }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()) }普通
function的参数类型和实际传参的类型要一致function参数为结构体指针, 传递进来的参数必须是结构体指针func ScaleFunc(v *Vertex, f float64) { v.X = v.X * f v.Y = v.Y * f }var v Vertex ScaleFunc(v, 5) // Compile error! ScaleFunc(&v, 5) // OKfunction参数为结构体,传递进来的参数必须是结构体func AbsFunc(v Vertex) float64 { return math.Sqrt(v.X*v.X + v.Y*v.Y) }var v Vertex fmt.Println(AbsFunc(v)) // OK fmt.Println(AbsFunc(&v)) // Compile error!Methods在调用时会做自动转换Calling a method with a pointer receiver by an object instead of a pointer to it?
receiver是结构体指针,v.Scale(5)as(&v).Scale(5)func (v *Vertex) Scale(f float64) { v.X = v.X * f v.Y = v.Y * f }var v Vertex v.Scale(5) // OK p := &v p.Scale(10) // OKreceiver是结构体,p.Abs()as(*p).Abs()func (v Vertex) Abs() float64 { return math.Sqrt(v.X*v.X + v.Y*v.Y) }var v Vertex fmt.Println(v.Abs()) // OK p := &v fmt.Println(p.Abs()) // OKreceiver为结构体指针的情况更常用,推荐可以修改
receiver本身的值
每次调用都会对receiver进行值拷贝, 拷贝结构体指针比拷贝结构体更节省资源,效率更高 -
Interfaces: Methods signatures
接口是隐式实现的,不用
implements等关键字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() }接口可以作为函数参数类型,未赋值时是
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) }(<nil>, <nil>) panic: runtime error: invalid memory address or nil pointer dereference [signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x49a62f]指针和接口的一些疑问
理解 Go interface 的 5 个关键点
指针和接口-
参数类型是结构体,实际传参也是结构体。实际参数的值并没有发生改变
package main import "fmt" type I interface { Double() } type T struct { X int Y int } func (t T) Double() { t.X = t.X * 2 t.Y = t.Y * 2 } func main() { var i I = T{3,4} i.Double() fmt.Println(i) }正常,没话好说
-
参数类型是结构体指针,实际传参也是结构体指针。实际参数的值发生了改变
func (t *T) Double() { t.X = t.X * 2 t.Y = t.Y * 2 } func main() { var i I = &T{3,4} i.Double() fmt.Println(i) }正常,没话好说
-
参数类型是结构体,实际传参是结构体指针。实际参数的值并没有发生改变
func (t T) Double() { t.X = t.X * 2 t.Y = t.Y * 2 } func main() { var i I = &T{3,4} i.Double() fmt.Println(i) }正常,可以说一下
实际传到函数里面是结构体指针的复制品
虽然是赋值品,由于是指针的关系,可以间接找到真正的结构体
只要编译器可以通过手段找到真正的结构体,就能正常调用
毕竟go语言的哲学是简洁、灵活 -
参数类型是结构体指针,实际传参是结构体
func (t *T) Double() { t.X = t.X * 2 t.Y = t.Y * 2 } func main() { var i I = T{3,4} i.Double() fmt.Println(i) }cannot use T literal (type T) as type I in assignment: T does not implement I (Double method has pointer receiver)编译错误,着重讲一下
实际传到函数里面是结构体的复制品
靠谱的解释(还是有点隐晦)
对于 T{3,4} 来说,这意味着 Double 方法会接受一个全新的 T{},因为方法的参数是 *T,编译器不会无中生有创建一个新的指针;即使编译器可以创建新指针,这个指针指向的也不是最初调用该方法的结构体
不妨换个思路,假设一下如果编译通过并正常调用会出现什么情况?
最终会输出
{3 4}
外面的实际参数的值并没有发生改变
因为Double内部实际更改的是结构体复制品的值这样会带来怎样的后果?
开发者估计傻眼了。接收者不是一个指针的类型吗?为什么外面
I变量的值没有跟着改变?
go语言的哲学是简洁、灵活。一连串的疑问与go语言的哲学理念就相违背了。
所以还不如直接编译不通过,将众多疑问扼杀在摇篮中。
空接口: 用来处理未知数据类型的参数
空接口可以接受任何数据类型的参数(任何数据类型都有0个Methods)
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)类型断言
检查接口变量
i是否为nil
检查接口变量i存储的值是否为某个类型
如果类型不相符,ok为false,并且转换之后的值为默认值。并不会发生panicpackage 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) }hello hello true 0 false panic: interface conversion: interface {} is string, not float64Type switches
可以很方便对接口的数据类型求
switchpackage 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!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) }Exercise: Stringers
package main import ( "fmt" "strings" ) type IPAddr [4]byte // TODO: Add a "String() string" method to IPAddr. func (ip IPAddr) String() string { s := make([]string, len(ip)) for i, val := range ip { s[i] = fmt.Sprint(int(val)) } return strings.Join(s, ".") } 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) } }Errors接口函数通常会返回
error。当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) } }Exercise: Errors
package main import ( "fmt" "math" ) type ErrNegativeSqrt float64 func (e ErrNegativeSqrt) Error() string { return fmt.Sprintf("cannot Sqrt negative number: %f", e) } const e = 1e-8 // small delta func Sqrt(x float64) (float64, error) { if x < 0 { return 0, ErrNegativeSqrt(x) } z := x // starting point for { new_z := z - ((z*z - x) / (2*z)) if math.Abs(new_z - z) < e { return new_z, nil } z = new_z } } func main() { fmt.Println(Sqrt(2)) fmt.Println(Sqrt(-2)) }Readers接口读取
streampackage 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 } } } -