Go 之旅四: 方法与接口篇
原文链接 http://ironxu.com/701 本文是学习 A Tour of Go (中文参考 Go 之旅中文 ) 整理的笔记,介绍Go 语言方法,接口,类型的基本概念和使用。 1. 方法
/** * go 语言 方法 */
package main
import (
"fmt"
"math"
)
type Vertex struct {
X,Y float64
}
/** * 方法名: Abs_method * 方法接收者: Vertex */
func (v Vertex) Abs_method() 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 Abs_function(v Vertex) float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
// 为非结构体声明方法
type MyFloat float64
func (f MyFloat) Abs_myfloat() float64 {
if f < 0 {
return float64(-f)
}
return float64(f)
}
func main() {
v := Vertex{3,4}
v.Scale(10) // 此时 v.Scale(10) 会隐式转为 (&v).Scale(10)
fmt.Println(v.Abs_method())
fmt.Println(Abs_function(v)) // 方法只是个带接收者参数的函数
f := MyFloat(-2)
fmt.Println(f.Abs_myfloat())
}
Go 没有类。不过你可以为结构体类型定义方法。 方法是一类带特殊的 1.1 非结构体类型声明方法只能为在同一包内定义的类型添加方法, 而不能为其它包内定义的类型(包括 type MyFloat float64
func (f MyFloat) Abs_myfloat () float64 {
if f < 0 {
return float64(-f)
}
return float64(f)
}
1.2 指针接收者为指针接收者声明方法:对于某类型 指针接收者的方法可以修改接收者指向的值。 由于方法经常需要修改它的接收者,指针接收者比值接收者更常用。若使用值接收者,方法只会对原始值的副本进行操作。 1.3 方法与指针重定向(隐式转换)以指针为接收者的方法被调用时,接收者既能为值又能为指针: func (v *Vertex) Scale(f float64) {} v.Scale(5) (&v).Scale(5)
由于 而以值为接收者的方法被调用时,接收者既能为值又能为指针: var v Vertex
fmt.Println(v.Abs()) // OK
p := &v
fmt.Println(p.Abs()) // OK
这种情况下,方法调用 1.4 选择值或指针作为接收者使用指针接收者的原因有二:
通常来说,所有给定类型的方法都应该有值或指针接收者,但并不应该二者混用。 2. 接口
/** * go 接口 */
package main
import (
"fmt"
"math"
)
// 定义接口
type Abser interface {
Abs() float64
}
func main() {
// 使用接口
var a Abser
f := MyFloat(-math.Sqrt2)
a = f
fmt.Println(a.Abs())
v := Vertex{3, 4}
a = &v
fmt.Println(a.Abs())
var i I
var t *T
i = t
i.M()
i = &T{"hello"}
i.M()
// 空接口
var inter_empty interface{}
inter_empty = 42
fmt.Printf("%v,%Tn",inter_empty,inter_empty)
inter_empty = "hello"
fmt.Printf("%v,inter_empty)
// 类型断言
var j interface{} = "hello"
s := j.(string)
fmt.Println(s)
s,ok := j.(string)
fmt.Println(s,ok)
inter_float,ok := j.(float64)
fmt.Println(inter_float,ok)
// 类型选择
do(21)
do("hello")
do(true)
}
type MyFloat float64
// 实现接口
func (f MyFloat) Abs() float64 {
if f < 0 {
return float64(-f)
}
return float64(f)
}
type Vertex struct {
X,Y float64
}
func (v *Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
type I interface {
M()
}
type T struct {
S string
}
func (t *T) M() {
if t == nil {
fmt.Println("<nil>")
return
}
fmt.Println(t.S)
}
类型通过实现一个接口的所有方法来实现该接口,既然无需专门显式声明,也就没有“implements“关键字。隐式接口将接口的实现与定义解耦,这样接口的实现可以出现在任何包中,无需提前定义。 2.1 接口值在内部,接口值可以看做包含值和具体类型的元组: (value,type)
接口值保存了一个具体底层类型的具体值,接口值调用方法时会调用具体类型的的同名方法。 2.2 底层值为 nil 的接口值即便接口内的具体值为 但是接口值 func main() {
var i I
i.M() // panic: runtime error
var t *T
i = t
i.M() // <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)
}
2.3 空接口指定了零个方法的接口值被称为空接口: interface{}
因为每个类型都至少实现了零个方法,空接口可保存任何类型的值。 2.4 类型断言类型断言提供了访问接口值底层具体值的方式。 t := i.(T)
该语句断言接口值 为了判断一个接口值是否保存了一个特定的类型, 类型断言可返回两个值:其底层值和判断断言是否成功的布尔值。 t,ok := i.(T)
若 2.5 类型选择类型选择是一种按顺序从几个类型断言中选择分支的结构。 类型选择与一般的 switch v := i.(type) {
case T:
// v 的类型为 T
case S:
// v 的类型为 S
default:
// 没有匹配,v 与 i 的类型相同
}
类型选择中的声明与类型断言 此选择语句判断接口值 3. Stringer
/** * go 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{"Author",42}
z := Person{"Modifier",1989}
fmt.Println(a,z)
}
type Stringer interface { String() string }
4. 错误
/** * go语言 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)
}
}
Go 程序使用 type error interface { Error() string }
通常函数会返回一个 error 值,调用的它的代码应当判断这个错误是否等于 nil 来进行错误处理。 i,err := strconv.Atoi("42")
if err != nil {
fmt.Printf("couldn't convert number: %vn",err)
return
}
fmt.Println("Converted integer:",i)
5. Reader
/** * go read */
package main
import (
"fmt"
"io"
"strings"
)
func main() {
r := strings.NewReader("Hello,workd!")
b := make([]byte,4)
for {
n,err := r.Read(b)
fmt.Printf("n = %v,err = %v b = %vn",n,err,b)
fmt.Printf("b[:n] = %qn",b[:n])
if err == io.EOF {
break
}
}
}
func (T) Read(b []byte) (n int,err error)
参考
可以关注我的微博了解更多信息: @刚刚小码农 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |