从c到golang(3.方法与指针)
从 C 到 golang
方法与指针
Go中没有类的概念,用结构体承担相应的工作
为任意类型声明方法 func (t Type) Abs() float64
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
为任意类型添加方法时,区分(t Type)和 (t *Type),前者为对象的拷贝,后者为对象的引用,需要修改时需要用后者,见下例
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) Scale1(f float64) {
v.X = v.X * f
v.Y = v.Y * f
}
func (v Vertex) Scale2(f float64) {
v.X = v.X * f
v.Y = v.Y * f
}
func main() {
v1 := Vertex{3, 4}
v1.Scale1(10)
v2 := Vertex{3, 4}
v2.Scale2(10)
fmt.Println(v1.Abs())
fmt.Println(v2.Abs())
}
输出
50
5
区分go中的方法(methods)与函数(function)
package main
import (
"fmt"
"math"
)
type Vertex struct {
X, Y float64
}
func Abs1(v Vertex) float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
func (v *Vertex) Abs2() float64{
v.X = 1
v.Y = 2
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
func main() {
v := Vertex{3, 4}
fmt.Println(Abs1(v))
fmt.Println(v.Abs2())
fmt.Println(Abs1(v))
}
输出
5
2.23606797749979
2.23606797749979
使用指针来更有效地进行操作结构体
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 main() {
v := &Vertex{3, 4}
v.Scale(5)
fmt.Println(v)
fmt.Println(v.X,v.Y)
}
输出
&{15 20}
15 20
类型通过实现一个接口的所有方法来实现该接口
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()
}
输出
hello
空接口可以给任意值,所以常用空接口来处理未知类型
var i interface{} = "anyString"
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)
使用断言判断接口是否保存了某种类型,因为见上一条,接口可保存任意数据类型,格式如t := i.(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)
}
接口类型判断,接近于断言的语法(改T为关键字type),使用i.(type)
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!
fmt
包中定义Stringer
接口,通过实现该接口中的String()
方法可以修改fmt.Println
中的输出内容
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)
类似于Stringer接口,同样内置一个error
接口,如下
type error interface {
Error() string
}
使用时需要做非空判断,注意其中的run方法,接口的犀利之处
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)
}
//注意该方法因为error的返回值,即使return了一个MyError对象的指针,拿到的仍是error对象,因为MyError实现了error的接口,所以可以将Myerror对象指向error.类似于多态.
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
观察下面这个例子,Go 指南中关于错误的例子
实现一个Sqrt
函数,使其接受负数参数时返回error
package main
import (
"fmt"
)
type ErrNegativeSqrt float64
func (e ErrNegativeSqrt) Error() string {
return fmt.Sprintf("cannot Sqrt negative number: %f", float64(e))
}
func Sqrt(x float64) (float64, error) {
if x < 0 {
return x, ErrNegativeSqrt(x)
}
return 0, nil
}
func main() {
fmt.Println(Sqrt(2))
fmt.Println(Sqrt(-2))
}
return x, ErrNegativeSqrt(x)
该行,其实就是将x转为实现了Error的ErrNegativeSqrt类型,指向error返回
使用Reader读取字节流
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
}
}
}
输出
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] = ""
实现一个Reader类型,它产生一个 ASCII 字符 'A' 的无限流
package main
import "golang.org/x/tour/reader"
type MyReader struct{}
// TODO: 给 MyReader 添加一个 Read([]byte) (int, error) 方法
func (m MyReader) Read(b []byte) (int, error) {
for i := 0; i < len(b); i++ {
b[i] = 'A'
}
return len(b), nil
}
func main() {
reader.Validate(MyReader{})
}
版权属于:邢迪的平行时空
本文链接:https://xingdi.me/archives/39.html
本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可