W3Cschool
恭喜您成為首批注冊(cè)用戶
獲得88經(jīng)驗(yàn)值獎(jiǎng)勵(lì)
原文鏈接:https://gopl-zh.github.io/ch7/ch7-01.html
目前為止,我們看到的類型都是具體的類型。一個(gè)具體的類型可以準(zhǔn)確的描述它所代表的值,并且展示出對(duì)類型本身的一些操作方式:就像數(shù)字類型的算術(shù)操作,切片類型的取下標(biāo)、添加元素和范圍獲取操作。具體的類型還可以通過它的內(nèi)置方法提供額外的行為操作。總的來說,當(dāng)你拿到一個(gè)具體的類型時(shí)你就知道它的本身是什么和你可以用它來做什么。
在Go語言中還存在著另外一種類型:接口類型。接口類型是一種抽象的類型。它不會(huì)暴露出它所代表的對(duì)象的內(nèi)部值的結(jié)構(gòu)和這個(gè)對(duì)象支持的基礎(chǔ)操作的集合;它們只會(huì)表現(xiàn)出它們自己的方法。也就是說當(dāng)你有看到一個(gè)接口類型的值時(shí),你不知道它是什么,唯一知道的就是可以通過它的方法來做什么。
在本書中,我們一直使用兩個(gè)相似的函數(shù)來進(jìn)行字符串的格式化:fmt.Printf,它會(huì)把結(jié)果寫到標(biāo)準(zhǔn)輸出,和fmt.Sprintf,它會(huì)把結(jié)果以字符串的形式返回。得益于使用接口,我們不必可悲的因?yàn)榉祷亟Y(jié)果在使用方式上的一些淺顯不同就必需把格式化這個(gè)最困難的過程復(fù)制一份。實(shí)際上,這兩個(gè)函數(shù)都使用了另一個(gè)函數(shù)fmt.Fprintf來進(jìn)行封裝。fmt.Fprintf這個(gè)函數(shù)對(duì)它的計(jì)算結(jié)果會(huì)被怎么使用是完全不知道的。
package fmt
func Fprintf(w io.Writer, format string, args ...interface{}) (int, error)
func Printf(format string, args ...interface{}) (int, error) {
return Fprintf(os.Stdout, format, args...)
}
func Sprintf(format string, args ...interface{}) string {
var buf bytes.Buffer
Fprintf(&buf, format, args...)
return buf.String()
}
Fprintf的前綴F表示文件(File)也表明格式化輸出結(jié)果應(yīng)該被寫入第一個(gè)參數(shù)提供的文件中。在Printf函數(shù)中的第一個(gè)參數(shù)os.Stdout是*os.File
類型;在Sprintf函數(shù)中的第一個(gè)參數(shù)&buf是一個(gè)指向可以寫入字節(jié)的內(nèi)存緩沖區(qū),然而它 并不是一個(gè)文件類型盡管它在某種意義上和文件類型相似。
即使Fprintf函數(shù)中的第一個(gè)參數(shù)也不是一個(gè)文件類型。它是io.Writer類型,這是一個(gè)接口類型定義如下:
package io
// Writer is the interface that wraps the basic Write method.
type Writer interface {
// Write writes len(p) bytes from p to the underlying data stream.
// It returns the number of bytes written from p (0 <= n <= len(p))
// and any error encountered that caused the write to stop early.
// Write must return a non-nil error if it returns n < len(p).
// Write must not modify the slice data, even temporarily.
//
// Implementations must not retain p.
Write(p []byte) (n int, err error)
}
io.Writer類型定義了函數(shù)Fprintf和這個(gè)函數(shù)調(diào)用者之間的約定。一方面這個(gè)約定需要調(diào)用者提供具體類型的值就像*os.File
和*bytes.Buffer
,這些類型都有一個(gè)特定簽名和行為的Write的函數(shù)。另一方面這個(gè)約定保證了Fprintf接受任何滿足io.Writer接口的值都可以工作。Fprintf函數(shù)可能沒有假定寫入的是一個(gè)文件或是一段內(nèi)存,而是寫入一個(gè)可以調(diào)用Write函數(shù)的值。
因?yàn)閒mt.Fprintf函數(shù)沒有對(duì)具體操作的值做任何假設(shè),而是僅僅通過io.Writer接口的約定來保證行為,所以第一個(gè)參數(shù)可以安全地傳入一個(gè)只需要滿足io.Writer接口的任意具體類型的值。一個(gè)類型可以自由地被另一個(gè)滿足相同接口的類型替換,被稱作可替換性(LSP里氏替換)。這是一個(gè)面向?qū)ο蟮奶卣鳌?
讓我們通過一個(gè)新的類型來進(jìn)行校驗(yàn),下面*ByteCounter
類型里的Write方法,僅僅在丟棄寫向它的字節(jié)前統(tǒng)計(jì)它們的長(zhǎng)度。(在這個(gè)+=賦值語句中,讓len(p)的類型和*c
的類型匹配的轉(zhuǎn)換是必須的。)
gopl.io/ch7/bytecounter
type ByteCounter int
func (c *ByteCounter) Write(p []byte) (int, error) {
*c += ByteCounter(len(p)) // convert int to ByteCounter
return len(p), nil
}
因?yàn)?ByteCounter滿足io.Writer的約定,我們可以把它傳入Fprintf函數(shù)中;Fprintf函數(shù)執(zhí)行字符串格式化的過程不會(huì)去關(guān)注ByteCounter正確的累加結(jié)果的長(zhǎng)度。
var c ByteCounter
c.Write([]byte("hello"))
fmt.Println(c) // "5", = len("hello")
c = 0 // reset the counter
var name = "Dolly"
fmt.Fprintf(&c, "hello, %s", name)
fmt.Println(c) // "12", = len("hello, Dolly")
除了io.Writer這個(gè)接口類型,還有另一個(gè)對(duì)fmt包很重要的接口類型。Fprintf和Fprintln函數(shù)向類型提供了一種控制它們值輸出的途徑。在2.5節(jié)中,我們?yōu)镃elsius類型提供了一個(gè)String方法以便于可以打印成這樣"100°C" ,在6.5節(jié)中我們給*IntSet添加一個(gè)String方法,這樣集合可以用傳統(tǒng)的符號(hào)來進(jìn)行表示就像"{1 2 3}"。給一個(gè)類型定義String方法,可以讓它滿足最廣泛使用之一的接口類型fmt.Stringer:
package fmt
// The String method is used to print values passed
// as an operand to any format that accepts a string
// or to an unformatted printer such as Print.
type Stringer interface {
String() string
}
我們會(huì)在7.10節(jié)解釋fmt包怎么發(fā)現(xiàn)哪些值是滿足這個(gè)接口類型的。
練習(xí) 7.1: 使用來自ByteCounter的思路,實(shí)現(xiàn)一個(gè)針對(duì)單詞和行數(shù)的計(jì)數(shù)器。你會(huì)發(fā)現(xiàn)bufio.ScanWords非常的有用。
練習(xí) 7.2: 寫一個(gè)帶有如下函數(shù)簽名的函數(shù)CountingWriter,傳入一個(gè)io.Writer接口類型,返回一個(gè)把原來的Writer封裝在里面的新的Writer類型和一個(gè)表示新的寫入字節(jié)數(shù)的int64類型指針。
func CountingWriter(w io.Writer) (io.Writer, *int64)
練習(xí) 7.3: 為在gopl.io/ch4/treesort(§4.4)中的*tree類型實(shí)現(xiàn)一個(gè)String方法去展示tree類型的值序列。
Copyright©2021 w3cschool編程獅|閩ICP備15016281號(hào)-3|閩公網(wǎng)安備35020302033924號(hào)
違法和不良信息舉報(bào)電話:173-0602-2364|舉報(bào)郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號(hào)
聯(lián)系方式:
更多建議: