<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                [TOC] ## 接口 Go中的接口為指定對象的行為提供了一種方式:如果事情可以*這樣*做,那么它就可以在*這里*使用。我們已經看到一些簡單的例子;自定義的打印可以通過`String`方法來實現,而`Fprintf`可以通過`Write`方法輸出到任意的地方。只有一個或兩個方法的接口在Go代碼中很常見,并且它的名字通常來自這個方法,例如實現`Write`的`io.Writer`。 類型可以實現多個接口。例如,如果一個集合實現了`sort.Interface`,其包含`Len()`,`Less(i, j int) bool`和`Swap(i, j int)`,那么它就可以通過程序包`sort`中的程序來進行排序,同時它還可以有一個自定義的格式器。在這個人造的例子中,`Sequence`同時符合這些條件。 ~~~ type Sequence []int // Methods required by sort.Interface. func (s Sequence) Len() int { return len(s) } func (s Sequence) Less(i, j int) bool { return s[i] < s[j] } func (s Sequence) Swap(i, j int) { s[i], s[j] = s[j], s[i] } // Method for printing - sorts the elements before printing. func (s Sequence) String() string { sort.Sort(s) str := "[" for i, elem := range s { if i > 0 { str += " " } str += fmt.Sprint(elem) } return str + "]" } ~~~ ## 轉換 `Sequence`的`String`方法重復了`Sprint`對切片所做的工作。如果我們在調用`Sprint`之前,將`Sequence`轉換為普通的`[]int`,則可以共享所做的工作。 ~~~ func (s Sequence) String() string { sort.Sort(s) return fmt.Sprint([]int(s)) } ~~~ 這個對象方法算是轉換技術的另一個例子,從`String`方法中安全地調用`Sprintf`。因為如果我們忽略類型名字,這兩個類型(`Sequence`和`[]int`)是相同的,在它們之間進行轉換是合法的。該轉換并不創建新的值,只不過是暫時使現有的值具有一個新的類型。(有其它的合法轉換,像整數到浮點,是會創建新值的。) 將表達式的類型進行轉換,來訪問不同的方法集合,這在Go程序中是一種常見用法。例如,我們可以使用已有類型`sort.IntSlice`來將整個例子簡化成這樣: ~~~ type Sequence []int // Method for printing - sorts the elements before printing func (s Sequence) String() string { sort.IntSlice(s).Sort() return fmt.Sprint([]int(s)) } ~~~ 現在,`Sequence`沒有實現多個接口(排序和打印),相反的,我們利用了能夠將數據項轉換為多個類型(`Sequence`,`sort.IntSlice`和`[]int`)的能力,每個類型完成工作的一部分。這在實際中不常見,但是卻可以很有效。 ## 接口轉換和類型斷言 [類型switch](http://www.hellogcc.org/effective_go.html#type_switch)為一種轉換形式:它們接受一個接口,在switch的每個case中,從某種意義上將其轉換為那種case的類型。這里有一個簡化版本,展示了`fmt.Printf`中的代碼如何使用類型switch將一個值轉換為字符串。如果其已經是字符串,那么我們想要接口持有的實際字符串值,如果其有一個`String`方法,則我們想要調用該方法的結果。 ~~~ type Stringer interface { String() string } var value interface{} // Value provided by caller. switch str := value.(type) { case string: return str case Stringer: return str.String() } ~~~ 第一種情況找到一個具體的值;第二種將接口轉換為另一個。使用這種方式進行混合類型完全沒有問題。 如果我們只關心一種類型該如何做?如果我們知道值為一個`string`,只是想將它抽取出來該如何做?只有一個case的類型switch是可以的,不過也可以用*類型斷言*。類型斷言接受一個接口值,從中抽取出顯式指定類型的值。其語法借鑒了類型switch子句,不過是使用了顯式的類型,而不是`type`關鍵字: ~~~ value.(typeName) ~~~ 結果是一個為靜態類型`typeName`的新值。該類型或者是一個接口所持有的具體類型,或者是可以被轉換的另一個接口類型。要抽取我們已知值中的字符串,可以寫成: ~~~ str := value.(string) ~~~ 不過,如果該值不包含一個字符串,則程序會產生一個運行時錯誤。為了避免這樣,可以使用“comma, ok”的習慣用法來安全地測試值是否為一個字符串: ~~~ str, ok := value.(string) if ok { fmt.Printf("string value is: %q\n", str) } else { fmt.Printf("value is not a string\n") } ~~~ 如果類型斷言失敗,則`str`將依然存在,并且類型為字符串,不過其為零值,一個空字符串。 這里有一個`if`-`else`語句的實例,其效果等價于這章開始的類型switch例子。 ~~~ if str, ok := value.(string); ok { return str } else if str, ok := value.(Stringer); ok { return str.String() } ~~~ ## 概述 如果一個類型只是用來實現接口,并且除了該接口以外沒有其它被導出的方法,那就不需要導出這個類型。只導出接口,清楚地表明了其重要的是行為,而不是實現,并且其它具有不同屬性的實現可以反映原始類型的行為。這也避免了對每個公共方法實例進行重復的文檔介紹。 這種情況下,構造器應該返回一個接口值,而不是所實現的類型。作為例子,在hash庫里,`crc32.NewIEEE`和`adler32.New`都是返回了接口類型`hash.Hash32`。在Go程序中,用CRC-32算法來替換Adler-32,只需要修改構造器調用;其余代碼都不受影響。 類似的方式可以使得在不同`crypto`程序包中的流密碼算法,可以與鏈在一起的塊密碼分離開。`crypto/cipher`程序包中的`Block`接口,指定了塊密碼的行為,即提供對單個數據塊的加密。然后,根據`bufio`程序包類推,實現該接口的加密包可以用于構建由`Stream`接口表示的流密碼,而無需知道塊加密的細節。 `crypto/cipher`接口看起來是這樣的: ~~~ type Block interface { BlockSize() int Encrypt(src, dst []byte) Decrypt(src, dst []byte) } type Stream interface { XORKeyStream(dst, src []byte) } ~~~ 這里有一個計數器模式(CTR)流的定義,其將塊密碼轉換為流密碼;注意塊密碼的細節被抽象掉了: ~~~ // NewCTR returns a Stream that encrypts/decrypts using the given Block in // counter mode. The length of iv must be the same as the Block's block size. func NewCTR(block Block, iv []byte) Stream ~~~ `NewCTR`并不只是用于一個特定的加密算法和數據源,而是用于任何對`Block`接口的實現和任何`Stream`。因為它們返回接口值,所以將CTR加密替換為其它加密模式只是一個局部的改變。構造器調用必須被修改,不過因為上下文代碼必須將結果只作為`Stream`來處理,所以其不會注意到差別。 ## 接口和方法 由于幾乎任何事物都可以附加上方法,所以幾乎任何事物都能夠滿足接口的要求。一個示例是在`http`程序包中,其定義了`Handler`接口。任何實現了`Handler`的對象都可以為HTTP請求提供服務。 ~~~ type Handler interface { ServeHTTP(ResponseWriter, *Request) } ~~~ `ResponseWriter`本身是一個接口,提供了對用于向客戶端返回響應的方法的訪問。這些方法包括了標準的`Write`方法,所以任何可以使用`io.Writer`的地方,都可以使用`http.ResponseWriter`。 簡單起見,讓我們忽略POST,假設HTTP請求總是GET;這種簡化不影響建立處理的方式。這里有一個簡單而完整的handler實現,用于計算頁面的訪問次數。 ~~~ // Simple counter server. type Counter struct { n int } func (ctr *Counter) ServeHTTP(w http.ResponseWriter, req *http.Request) { ctr.n++ fmt.Fprintf(w, "counter = %d\n", ctr.n) } ~~~ (題外話,注意`Fprintf`是如何能夠打印到`http.ResponseWriter`的。)作為參考,下面給出了如何將該服務附加到URL樹上的節點。 ~~~ import "net/http" ... ctr := new(Counter) http.Handle("/counter", ctr) ~~~ 但是為什么`Counter`為一個結構體?只需要一個整數就可以了。(接收者需要為一個指針,這樣增量才能對調用者可見。) ~~~ // Simpler counter server. type Counter int func (ctr *Counter) ServeHTTP(w http.ResponseWriter, req *http.Request) { *ctr++ fmt.Fprintf(w, "counter = %d\n", *ctr) } ~~~ 如果你的程序具有某個內部狀態,當頁面被訪問時需要被告知,那么該如何?可以將一個channel綁定到網頁上。 ~~~ // A channel that sends a notification on each visit. // (Probably want the channel to be buffered.) type Chan chan *http.Request func (ch Chan) ServeHTTP(w http.ResponseWriter, req *http.Request) { ch <- req fmt.Fprint(w, "notification sent") } ~~~ 最后,比方說我們想在`/args`上展現我們喚起服務二進制時所使用的參數。這很容易編寫一個函數來打印參數。 ~~~ func ArgServer() { fmt.Println(os.Args) } ~~~ 我們怎么將它轉換成HTTP服務?我們可以將`ArgServer`創建為某個類型的方法,忽略該類型的值,不過有一種更干凈的方式。既然我們可以為除了指針和接口以外的任何類型來定義方法,那么我們可以為函數編寫一個方法。`http`程序包包含了這樣的代碼: ~~~ // The HandlerFunc type is an adapter to allow the use of // ordinary functions as HTTP handlers. If f is a function // with the appropriate signature, HandlerFunc(f) is a // Handler object that calls f. type HandlerFunc func(ResponseWriter, *Request) // ServeHTTP calls f(c, req). func (f HandlerFunc) ServeHTTP(w ResponseWriter, req *Request) { f(w, req) } ~~~ `HandlerFunc`為一個類型,其具有一個方法,`ServeHTTP`,所以該類型值可以為HTTP請求提供服務。看下該方法的實現:接收者為一個函數,`f`,并且該方法調用了`f`。這看起來可能有些怪異,但是這與接收者為channel,方法在channel上進行發送數據并無差別。 要將`ArgServer`放到HTTP服務中,我們首先將其簽名修改正確。 ~~~ // Argument server. func ArgServer(w http.ResponseWriter, req *http.Request) { fmt.Fprintln(w, os.Args) } ~~~ `ArgServer`現在具有和`HandlerFunc`相同的簽名,所以其可以被轉換為那個類型,然后訪問它的方法,就像我們將`Sequence`轉換為`IntSlice`,來訪問`IntSlice.Sort`一樣。代碼實現很簡潔: ~~~ http.Handle("/args", http.HandlerFunc(ArgServer)) ~~~ 當有人訪問頁面`/args`時,在該頁上安裝的處理者就具有值`ArgServer`和類型`HandlerFunc`。HTTP服務將會調用該類型的方法`ServeHTTP`,將`ArgServer`作為接收者,其將轉而調用`ArgServer`(通過在`HandlerFunc.ServeHTTP`內部調用`f(c, req)`)。然后,參數就被顯示出來了。 在這章節,我們分別通過結構體,整數,channel,以及函數創建了HTTP服務,這都是因為接口就是一個方法的集合,其可以針對(幾乎)任何類型來定義。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看