9.蛤蟆笔记go语言——方法和接口

notbaron · · 1369 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

9.蛤蟆笔记go语言——方法和接口

方法

Go 没有类。然而,仍然可以在结构体类型上定义方法。

方法接收者 出现在 func 关键字和方法名之间的参数中。

package main

import (

         "fmt"

         "math"

)

type Vertex struct {

         X, Yfloat64

}

func (v *Vertex) Abs() float64 {

         returnmath.Sqrt(v.X*v.X + v.Y*v.Y)

}

func main() {

         v :=&Vertex{3, 4}

         fmt.Println(v.Abs())

}

执行:

         5

你可以对包中的 任意 类型定义任意方法,而不仅仅是针对结构体。

但是,不能对来自其他包的类型或基础类型定义方法。

package main

import (

         "fmt"

         "math"

)

type MyFloat float64

func (f MyFloat) Abs() float64 {

         if f< 0 {

                   returnfloat64(-f)

         }

         returnfloat64(f)

}

func main() {

         f :=MyFloat(-math.Sqrt2)

         fmt.Println(f.Abs())

}

执行结果:

1.4142135623730951

接收者为指针的方法

方法可以与命名类型或命名类型的指针关联。

刚刚看到的两个 Abs 方法。一个是在 *Vertex 指针类型上,而另一个在 MyFloat 值类型上。有两个原因需要使用指针接收者。首先避免在每个方法调用中拷贝值(如果值类型是大的结构体的话会更有效率)。其次,方法可以修改接收者指向的值。

尝试修改 Abs 的定义,同时 Scale 方法使用 Vertex 代替 *Vertex 作为接收者。

当 v 是 Vertex 的时候 Scale 方法没有任何作用。`Scale` 修改 `v`。当 v 是一个值(非指针),方法看到的是 Vertex 的副本,并且无法修改原始值。

Abs 的工作方式是一样的。只不过,仅仅读取`v`。所以读取的是原始值(通过指针)还是那个值的副本并没有关系。

package main

import (

         "fmt"

         "math"

)

type Vertex struct {

         X, Yfloat64

}

func (v *Vertex) Scale(f float64) {

         v.X =v.X * f

         v.Y =v.Y * f

}

func (v *Vertex) Abs() float64 {

         returnmath.Sqrt(v.X*v.X + v.Y*v.Y)

}

func main() {

         v :=&Vertex{3, 4}

         v.Scale(5)

         fmt.Println(v,v.Abs())

}

执行:

         &{1520} 25

接口

接口类型是由一组方法定义的集合。

接口类型的值可以存放实现这些方法的任何值。

注意: 列子代码的 22 行存在一个错误。由于 Abs 只定义在 *Vertex(指针类型) 上,所以 Vertex(值类型) 不满足 `Abser`。

package main

import (

         "fmt"

         "math"

)

type Abser interface {

         Abs()float64

}

func main() {

         var aAbser

         f :=MyFloat(-math.Sqrt2)

         v :=Vertex{3, 4}

 

         a =f  // a MyFloat 实现了 Abser

         a =&v // a *Vertex 实现了Abser

 

         // 下面一行,v 是一个 Vertex(而不是 *Vertex

         // 所以没有实现 Abser

         a = v

 

         fmt.Println(a.Abs())

}

type MyFloat float64

func (f MyFloat) Abs() float64 {

         if f< 0 {

                   returnfloat64(-f)

         }

         returnfloat64(f)

}

type Vertex struct {

         X, Yfloat64

}

func (v *Vertex) Abs() float64 {

         returnmath.Sqrt(v.X*v.X + v.Y*v.Y)

}

         #command-line-arguments

         .\hello.go:22:cannot use v (type Vertex) as type Abser in assignment:

                   Vertexdoes not implement Abser (Abs method has pointer receiver)

         exitstatus 2

隐式接口

类型通过实现那些方法来实现接口。没有显式声明的必要;所以也就没有关键字“implements“。

隐式接口解藕了实现接口的包和定义接口的包:互不依赖。

因此,也就无需在每一个实现上增加新的接口名称,这样同时也鼓励了明确的接口定义。

io 定义了 Reader 和 `Writer`;其实不一定要这么做。

package main

import (

         "fmt"

         "os"

)

type Reader interface {

         Read(b[]byte) (n int, err error)

}

type Writer interface {

         Write(b[]byte) (n int, err error)

}

type ReadWriter interface {

         Reader

         Writer

}

func main() {

         var wWriter

         //os.Stdout 实现了Writer

         w =os.Stdout

         fmt.Fprintf(w,"hello, writer\n")

}

执行:

         hello,writer

Stringers

一个普遍存在的接口是 fmt 包中定义的 Stringer

type Stringer struct {

    String() string

}

Stringer 是一个可以用字符串描述自己的类型。`fmt`包(还有许多其他包)使用这个来进行输出。

package main

import "fmt"

type Person struct {

         Namestring

         Age  int

}

func (p Person) String() string {

         returnfmt.Sprintf("%v (%v years)", p.Name, p.Age)

}

func main() {

         a := Person{"ArthurDent", 42}

         z :=Person{"Zaphod Beeblebrox", 9001}

         fmt.Println(a,z)

}

执行:

         ArthurDent (42 years) Zaphod Beeblebrox (9001 years)

错误

Go 程序使用 error 值来表示错误状态。

与 fmt.Stringer 类似,`error` 类型是一个内建接口:

type error interface {

   Error() string

}

(与 fmt.Stringer 类似,`fmt` 包在输出时也会试图匹配`error`。)

通常函数会返回一个 error 值,调用的它的代码应当判断这个错误是否等于 `nil`,来进行错误处理。

i, err := strconv.Atoi("42")

if err != nil {

   fmt.Printf("couldn't convert number: %v\n", err)

}

fmt.Println("Converted integer:",i)

error 为 nil 时表示成功;非 nil 的 error 表示错误。

package main

import (

         "fmt"

         "time"

)

type MyError struct {

         Whentime.Time

         Whatstring

}

func (e *MyError) Error() string {

         returnfmt.Sprintf("at %v, %s",

                   e.When,e.What)

}

func run() error {

         return&MyError{

                   time.Now(),

                   "itdidn't work",

         }

}

func main() {

         if err:= run(); err != nil {

                   fmt.Println(err)

         }

}

执行:

         at2016-06-16 23:10:58.7118248 +0800 CST, it didn't work

 

Readers

io 包指定了 io.Reader 接口, 它表示从数据流结尾读取。

Go 标准库包含了这个接口的许多实现, 包括文件、网络连接、压缩、加密等等。

io.Reader 接口有一个 Read 方法:

func (T) Read(b []byte) (n int, err error)

Read 用数据填充指定的字节 slice,并且返回填充的字节数和错误信息。 在遇到数据流结尾时,返回 io.EOF 错误。

例子代码创建了一个 strings.Reader。 并且以每次 8 字节的速度读取它的输出。

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])

                   iferr == 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]= ""


Web 服务器

http 通过任何实现了 http.Handler 的值来响应 HTTP 请求:

package http

 

type Handler interface {

   ServeHTTP(w ResponseWriter, r *Request)

}

在这个例子中,类型 Hello 实现了 `http.Handler`。

访问 http://localhost:4000/ 会看到来自程序的问候。

package main

import (

         "fmt"

         "log"

         "net/http"

)

type Hello struct{}

func (h Hello) ServeHTTP(

         whttp.ResponseWriter,

         r*http.Request) {

         fmt.Fprint(w,"Hello!")

}

func main() {

         var hHello

         err :=http.ListenAndServe("localhost:4000", h)

         if err!= nil {

                   log.Fatal(err)

         }

}

图片

Package image 定义了 Image 接口:

package image

 

type Image interface {

   ColorModel() color.Model

   Bounds() Rectangle

   At(x, y int) color.Color

}

*注意*:`Bounds` 方法的 Rectangle 返回值实际上是一个 image.Rectangle, 其定义在 image 包中。

(参阅文档了解全部信息。)

color.Color 和 color.Model 也是接口,但是通常因为直接使用预定义的实现 image.RGBA 和 image.RGBAModel 而被忽视了。这些接口和类型由image/color 定义。

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)

         00 0 0

 


有疑问加站长微信联系(非本文作者)

本文来自:CSDN博客

感谢作者:notbaron

查看原文:9.蛤蟆笔记go语言——方法和接口

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

1369 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传