Go 指南 方法和接口

一蝗砾、方法

Go 沒有類日熬。然而,仍然可以在結構體類型上定義方法盆繁。

方法接收者 出現(xiàn)在 func 關鍵字和方法名之間的參數(shù)中掀淘。

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())
}

你可以對包中的 任意 類型定義任意方法,而不僅僅是針對結構體油昂。

但是革娄,不能對來自其他包的類型或基礎類型定義方法倾贰。

方法可以與命名類型或命名類型的指針關聯(lián)。

剛剛看到的兩個 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, 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

二聪全、接口

接口類型是由一組方法定義的集合泊藕。

接口類型的值可以存放實現(xiàn)這些方法的任何值。

注意: 示例代碼的 22 行存在一個錯誤难礼。 由于 Abs 只定義在 *Vertex(指針類型)上娃圆, 所以 Vertex(值類型)不滿足 Abser

package main

import (
    "fmt"
    "math"
)

type Abser interface {
    Abs() float64
}

func main() {
    var a Abser
    f := MyFloat(-math.Sqrt2)
    v := Vertex{3, 4}

    a = f  // a MyFloat 實現(xiàn)了 Abser
    a = &v // a *Vertex 實現(xiàn)了 Abser

    // 下面一行蛾茉,v 是一個 Vertex(而不是 *Vertex)
    // 所以沒有實現(xiàn) Abser讼呢。
    a = v

    fmt.Println(a.Abs())
}

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

2.1 隱式接口

類型通過實現(xiàn)那些方法來實現(xiàn)接口。 沒有顯式聲明的必要谦炬;所以也就沒有關鍵字implements悦屏。

隱式接口解耦了實現(xiàn)接口的包和定義接口的包:互不依賴。

因此吧寺,也就無需在每一個實現(xiàn)上增加新的接口名稱窜管,這樣同時也鼓勵了明確的接口定義。

包 io 定義了 ReaderWriter稚机;其實不一定要這么做。

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 w Writer

    // os.Stdout 實現(xiàn)了 Writer
    w = os.Stdout

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

2.2 Stringers

一個普遍存在的接口是 fmt 包中定義的 Stringer获搏。

type Stringer interface {
    String() string
}

Stringer 是一個可以用字符串描述自己的類型赖条。fmt包 (還有許多其他包)使用這個來進行輸出失乾。類似Java中的toString()

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

2.3 錯誤

Go 程序使用 error 值來表示錯誤狀態(tài)纬乍。

fmt.Stringer 類似碱茁, error 類型是一個內建接口:

type error interface {
    Error() string
}

與 fmt.Stringer 類似,fmt 包在輸出時也會試圖匹配 error仿贬。

通常函數(shù)會返回一個 error 值纽竣,調用的它的代碼應當判斷這個錯誤是否等于 nil, 來進行錯誤處理茧泪。

i, err := strconv.Atoi("42")
if err != nil {
    fmt.Printf("couldn't convert number: %v\n", err)
    return
}
fmt.Println("Converted integer:", i)

errornil 時表示成功蜓氨;非 nilerror 表示錯誤。

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

2.4 Readers

io 包指定了 io.Reader 接口队伟, 它表示從數(shù)據(jù)流結尾讀取穴吹。

Go 標準庫包含了這個接口的許多實現(xiàn), 包括文件嗜侮、網絡連接港令、壓縮、加密等等锈颗。

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

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

Read 用數(shù)據(jù)填充指定的字節(jié) slice顷霹,并且返回填充的字節(jié)數(shù)和錯誤信息。 在遇到數(shù)據(jù)流結尾時,返回 io.EOF 錯誤侣夷。

例子代碼創(chuàng)建了一個 strings.Reader瑰步。 并且以每次 8 字節(jié)的速度讀取它的輸出。

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
        }
    }
}

三绅喉、Web 服務器

http 通過任何實現(xiàn)了 http.Handler 的值來響應 HTTP 請求:

package http

type Handler interface {
    ServeHTTP(w ResponseWriter, r *Request)
}

在這個例子中,類型 Hello 實現(xiàn)了 http.Handler叫乌。

訪問 http://localhost:4000/ 會看到來自程序的問候柴罐。

package main

import (
    "fmt"
    "log"
    "net/http"
)

type Hello struct{}

func (h Hello) ServeHTTP(
    w http.ResponseWriter,
    r *http.Request) {
    fmt.Fprint(w, "Hello!")
}

func main() {
    var h Hello
    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.Colorcolor.Model 也是接口排宰,但是通常因為直接使用預定義的實現(xiàn) image.RGBAimage.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())
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末板甘,一起剝皮案震驚了整個濱河市党瓮,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌盐类,老刑警劉巖寞奸,帶你破解...
    沈念sama閱讀 216,744評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件呛谜,死亡現(xiàn)場離奇詭異,居然都是意外死亡枪萄,警方通過查閱死者的電腦和手機隐岛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,505評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來瓷翻,“玉大人聚凹,你說我怎么就攤上這事∑胫悖” “怎么了妒牙?”我有些...
    開封第一講書人閱讀 163,105評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長童谒。 經常有香客問我单旁,道長,這世上最難降的妖魔是什么饥伊? 我笑而不...
    開封第一講書人閱讀 58,242評論 1 292
  • 正文 為了忘掉前任象浑,我火速辦了婚禮,結果婚禮上琅豆,老公的妹妹穿的比我還像新娘愉豺。我一直安慰自己,他們只是感情好茫因,可當我...
    茶點故事閱讀 67,269評論 6 389
  • 文/花漫 我一把揭開白布蚪拦。 她就那樣靜靜地躺著,像睡著了一般冻押。 火紅的嫁衣襯著肌膚如雪驰贷。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,215評論 1 299
  • 那天洛巢,我揣著相機與錄音括袒,去河邊找鬼。 笑死稿茉,一個胖子當著我的面吹牛锹锰,可吹牛的內容都是我干的。 我是一名探鬼主播漓库,決...
    沈念sama閱讀 40,096評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼恃慧,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了渺蒿?” 一聲冷哼從身側響起痢士,我...
    開封第一講書人閱讀 38,939評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎茂装,沒想到半個月后良瞧,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體陪汽,經...
    沈念sama閱讀 45,354評論 1 311
  • 正文 獨居荒郊野嶺守林人離奇死亡训唱,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,573評論 2 333
  • 正文 我和宋清朗相戀三年褥蚯,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片况增。...
    茶點故事閱讀 39,745評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡赞庶,死狀恐怖,靈堂內的尸體忽然破棺而出澳骤,到底是詐尸還是另有隱情歧强,我是刑警寧澤,帶...
    沈念sama閱讀 35,448評論 5 344
  • 正文 年R本政府宣布为肮,位于F島的核電站摊册,受9級特大地震影響,放射性物質發(fā)生泄漏颊艳。R本人自食惡果不足惜茅特,卻給世界環(huán)境...
    茶點故事閱讀 41,048評論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望棋枕。 院中可真熱鬧白修,春花似錦、人聲如沸重斑。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,683評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽窥浪。三九已至祖很,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間漾脂,已是汗流浹背假颇。 一陣腳步聲響...
    開封第一講書人閱讀 32,838評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留符相,地道東北人拆融。 一個月前我還...
    沈念sama閱讀 47,776評論 2 369
  • 正文 我出身青樓,卻偏偏與公主長得像啊终,于是被迫代替她去往敵國和親镜豹。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,652評論 2 354

推薦閱讀更多精彩內容

  • 出處---Go編程語言 歡迎來到 Go 編程語言指南蓝牲。本指南涵蓋了該語言的大部分重要特性 Go 語言的交互式簡介趟脂,...
    Tuberose閱讀 18,426評論 1 46
  • 方法和接口 第四篇包含了方法和接口,可以用它們來定義對象和其行為例衍;以及如何將所有內容貫通起來昔期。 方法 Go 沒有類...
    張洋銘Ocean閱讀 1,482評論 2 0
  • 原文鏈接 http://ironxu.com/701 本文是學習 A Tour of Go (中文參考 Go 之旅...
    好剛編程閱讀 764評論 0 7
  • Spring Cloud為開發(fā)人員提供了快速構建分布式系統(tǒng)中一些常見模式的工具(例如配置管理已卸,服務發(fā)現(xiàn),斷路器硼一,智...
    卡卡羅2017閱讀 134,652評論 18 139
  • 國家電網公司企業(yè)標準(Q/GDW)- 面向對象的用電信息數(shù)據(jù)交換協(xié)議 - 報批稿:20170802 前言: 排版 ...
    庭說閱讀 10,962評論 6 13