本篇內(nèi)容包括:
- 方法
方法
Go 沒有類。然而,仍然可以在結(jié)構(gòu)體類型上定義方法。
方法接收者 出現(xiàn)在 func 關(guān)鍵字和方法名之間的參數(shù)中。
type Vertex struct {
X, Y float64
}
// 類似于Java中點(diǎn)語法實(shí)現(xiàn)
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())
}
// 5
可以對包中的 任意 類型定義任意方法,而不僅僅是針對結(jié)構(gòu)體。
但是,不能對來自其他包的類型或基礎(chǔ)類型定義方法。
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())
}
接收者為指針的方法
方法可以與命名類型或命名類型的指針關(guān)聯(lián)。
剛剛看到的兩個(gè) Abs 方法。一個(gè)是在 *Vertex 指針類型上,而另一個(gè)在 MyFloat 值類型上。 有兩個(gè)原因需要使用指針接收者。首先避免在每個(gè)方法調(diào)用中拷貝值(如果值類型是大的結(jié)構(gòu)體的話會更有效率)。其次,方法可以修改接收者指向的值。
嘗試修改 Abs 的定義,同時(shí) Scale 方法使用 Vertex 代替 *Vertex 作為接收者。
當(dāng) v 是 Vertex 的時(shí)候 Scale 方法沒有任何作用。Scale 修改 v。當(dāng) v 是一個(gè)值(非指針),方法看到的是 Vertex 的副本,并且無法修改原始值。
Abs 的工作方式是一樣的。只不過,僅僅讀取 v。所以讀取的是原始值(通過指針)還是那個(gè)值的副本并沒有關(guān)系。
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}
v.Scale(5)
fmt.Println(v, v.Abs())
}
接口
接口類型是由一組方法定義的集合。
接口類型的值可以存放實(shí)現(xiàn)這些方法的任何值。
type Abser interface {
Abs() float64
}
func main() {
var a Abser
f := MyFloat(-math.Sqrt2)
v := Vertex{3, 4}
a = f // a MyFloat 實(shí)現(xiàn)了 Abser
a = &v // a *Vertex 實(shí)現(xiàn)了 Abser
// 下面一行,v 是一個(gè) Vertex(而不是 *Vertex)
// 所以沒有實(shí)現(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)
}
隱式接口
類型通過實(shí)現(xiàn)那些方法來實(shí)現(xiàn)接口。 沒有顯式聲明的必要;所以也就沒有關(guān)鍵字“implements“。
隱式接口解藕了實(shí)現(xiàn)接口的包和定義接口的包:互不依賴。
因此,也就無需在每一個(gè)實(shí)現(xiàn)上增加新的接口名稱,這樣同時(shí)也鼓勵(lì)了明確的接口定義
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 實(shí)現(xiàn)了 Writer
w = os.Stdout
fmt.Fprintf(w, "hello, writer\n")
}
Stringers
一個(gè)普遍存在的接口是 fmt 包中定義的 Stringer。
type Stringer struct {
String() string
}
Stringer 是一個(gè)可以用字符串描述自己的類型。fmt包 (還有許多其他包)使用這個(gè)來進(jìn)行輸出。
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)
錯(cuò)誤
Go 程序使用 error 值來表示錯(cuò)誤狀態(tài)。
與 fmt.Stringer 類似,error 類型是一個(gè)內(nèi)建接口:
type error interface {
Error() string
}
(與 fmt.Stringer 類似,`fmt` 包在輸出時(shí)也會試圖匹配 `error`。)
通常函數(shù)會返回一個(gè) error 值,調(diào)用的它的代碼應(yīng)當(dāng)判斷這個(gè)錯(cuò)誤是否等于 nil, 來進(jìn)行錯(cuò)誤處理。
i, err := strconv.Atoi("42")
if err != nil {
fmt.Printf("couldn't convert number: %v\n", err)
}
fmt.Println("Converted integer:", i)
error 為 nil 時(shí)表示成功;非 nil 的 error 表示錯(cuò)誤。
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)
}
}
// at 2018-04-18 23:21:57.492848045 +0800 CST m=+0.000363471, it didn't work
Reader
io 包指定了 io.Reader 接口, 它表示從數(shù)據(jù)流結(jié)尾讀取。
Go 標(biāo)準(zhǔn)庫包含了這個(gè)接口的許多實(shí)現(xiàn), 包括文件、網(wǎng)絡(luò)連接、壓縮、加密等等。
io.Reader 接口有一個(gè) Read 方法:
func (T) Read(b []byte) (n int, err error)</pre>
Read 用數(shù)據(jù)填充指定的字節(jié) slice,并且返回填充的字節(jié)數(shù)和錯(cuò)誤信息。 在遇到數(shù)據(jù)流結(jié)尾時(shí),返回 io.EOF 錯(cuò)誤。
例子代碼創(chuàng)建了一個(gè) strings.Reader。 并且以每次 8 字節(jié)的依次讀取它的輸出。
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] = ""
*/
利用 rot13 代換密碼對數(shù)據(jù)流進(jìn)行修改。
type rot13Reader struct {
r io.Reader
}
// 轉(zhuǎn)換byte 前進(jìn)13位/后退13位
func rot13(b byte) byte {
switch {
case 'A' <= b && b <= 'M':
b = b + 13
case 'M' < b && b <= 'Z':
b = b - 13
case 'a' <= b && b <= 'm':
b = b + 13
case 'm' < b && b <= 'z':
b = b - 13
}
return b
}
// 重寫Read方法
func (mr rot13Reader) Read(b []byte) (int, error) {
n, e := mr.r.Read(b)
for i := 0; i < n; i++ {
b[i] = rot13(b[i])
}
return n, e
}
func main() {
s := strings.NewReader("Lbh penpxrq gur pbqr!")
r := rot13Reader{s}
io.Copy(os.Stdout, &r)
}
Web 服務(wù)器
包 http 通過任何實(shí)現(xiàn)了 http.Handler 的值來響應(yīng) HTTP 請求:
package http
type Handler interface {
ServeHTTP(w ResponseWriter, r *Request)
}
在這個(gè)例子中,類型 Hello 實(shí)現(xiàn)了 http.Handler。
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)
}
}