一蝗砾、方法
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 定義了 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 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)
error
為 nil
時表示成功蜓氨;非 nil
的 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)
}
}
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.Color
和 color.Model
也是接口排宰,但是通常因為直接使用預定義的實現(xiàn) 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())
}