在Golang中,我們使用結構與接收方法。一切都完美到這裏。
但是我不確定接口是什麼。我們在結構體中定義方法,如果我們想在一個結構體上實現一個方法,我們仍然在另一個結構體中再次寫它。
這意味着接口似乎只是方法定義,只是在頁面上佔用額外的不必要空間。爲什麼Golang需要接口?
有沒有任何例子解釋爲什麼我需要一個接口?
在Golang中,我們使用結構與接收方法。一切都完美到這裏。
但是我不確定接口是什麼。我們在結構體中定義方法,如果我們想在一個結構體上實現一個方法,我們仍然在另一個結構體中再次寫它。
這意味着接口似乎只是方法定義,只是在頁面上佔用額外的不必要空間。爲什麼Golang需要接口?
有沒有任何例子解釋爲什麼我需要一個接口?
接口過於龐大,無法給出全面深入的答案,但有些事情要明確說明。
接口是工具。不管你是否使用它們都取決於你,但它們可以使代碼更清晰,並且它們可以在軟件包或客戶端(用戶)和服務器(提供者)之間提供良好的API。
是的,你可以創建自己的struct
類型,你可以在「附加」的方法給它,例如:
type Cat struct{}
func (c Cat) Say() string { return "meow" }
type Dog struct{}
func (d Dog) Say() string { return "woof" }
func main() {
c := Cat{}
fmt.Println("Cat says:", c.Say())
d := Dog{}
fmt.Println("Dog says:", d.Say())
}
我們已經可以看到上面的代碼中的一些重複:使得無論Cat
何時Dog
說些什麼。我們是否可以將兩者當作同一種實體處理,如動物?不是真的。當然,我們可以將兩者都作爲interface{}
來處理,但如果我們這樣做了,我們就不能調用它們的Say()
方法,因爲interface{}
類型的值沒有定義任何方法。
在上述兩種類型中都有一些相似:兩種方法都具有相同簽名(參數和結果類型)的方法Say()
。我們可以捕捉這與接口:
type Sayer interface {
Say() string
}
接口只包含簽名的方法,但不是他們的實現。
請注意,如果其中一個類型爲實現了一個接口,如果它的方法集是接口的超集。沒有宣言的意圖。這是什麼意思?我們以前的Cat
和Dog
類型已經實現了這個接口,即使這個接口定義在我們之前寫的時候並不存在,我們也沒有觸及它們來標記它們或者什麼。他們只是做。
接口指定行爲。實現接口的類型意味着該類型具有接口「規定」的所有方法。
由於兩者都執行Sayer
,我們可以將兩者都作爲Sayer
的值處理,它們有這個共同點。看看我們如何能夠團結同時處理:
animals := []Sayer{c, d}
for _, a := range animals {
fmt.Println(reflect.TypeOf(a).Name(), "says:", a.Say())
}
(反映部分是隻拿到類型名,沒有太大的它的現在。)
的重要組成部分,是我們可以將Cat
和Dog
作爲相同種類(接口類型)處理,並且使用它們/使用它們。如果你是迅速,以利用Say()
方法創建其他類型的,他們可以排隊Cat
和Dog
旁邊:
type Horse struct{}
func (h Horse) Say() string { return "neigh" }
animals = append(animals, Horse{})
for _, a := range animals {
fmt.Println(reflect.TypeOf(a).Name(), "says:", a.Say())
}
比方說,你要編寫這些類型的作品其他代碼。一個輔助函數:
func MakeCatTalk(c Cat) {
fmt.Println("Cat says:", c.Say())
}
是,上述功能可與Cat
,並沒有別的。如果你想要類似的東西,你必須爲每種類型編寫它。不用說這有多糟糕。
是的,你可以寫它採取的interface{}
參數,並使用type assertion或type switches,這將減少的輔助功能的數量,但仍然看起來非常難看。
解決方案?是的,接口。簡單地聲明函數把它定義你想用它做的行爲的接口類型的值,而這一切:
func MakeTalk(s Sayer) {
fmt.Println(reflect.TypeOf(s).Name(), "says:", s.Say())
}
你可以調用這個函數的Cat
值,Dog
,Horse
或任何其他類型不知道「,直到現在,它有一個Say()
方法。涼。
在Go Playground上嘗試使用這些示例。
,我將在這裏展示,接口兩個有趣的用例轉到:
1-看到這兩個簡單的接口:
type Reader interface {
Read(p []byte) (n int, err error)
}
type Writer interface {
Write(p []byte) (n int, err error)
}
使用這兩個簡單的接口,你可以做這個有趣的魔術:
package main
import (
"bufio"
"bytes"
"fmt"
"io"
"os"
"strings"
)
func main() {
file, err := os.Create("log.txt")
if err != nil {
panic(err)
}
defer file.Close()
w := io.MultiWriter(file, os.Stdout)
r := strings.NewReader("You'll see this string twice!!\n")
io.Copy(w, r)
slice := []byte{33, 34, 35, 36, 37, 38, 39, 10, 13}
io.Copy(w, bytes.NewReader(slice)) // !"#$%&'
buf := &bytes.Buffer{}
io.Copy(buf, bytes.NewReader(slice))
fmt.Println(buf.Bytes()) // [33 34 35 36 37 38 39 10 13]
_, err = file.Seek(0, 0)
if err != nil {
panic(err)
}
r = strings.NewReader("Hello\nWorld\nThis\nis\nVery\nnice\nInterfacing.\n")
rdr := io.MultiReader(r, file)
scanner := bufio.NewScanner(rdr)
for scanner.Scan() {
fmt.Println(scanner.Text())
}
}
輸出:
You'll see this string twice!!
!"#$%&'
[33 34 35 36 37 38 39 10 13]
Hello
World
This
is
Very
nice
Interfacing.
You'll see this string twice!!
!"#$%&'
我希望這個代碼是非常明顯的:
使用strings.NewReader
串讀取和使用io.MultiWriter
只io.Copy(w, r)
同時寫入兩個file
和os.Stdout
。然後使用bytes.NewReader(slice)
從片中讀取並同時寫入file
和os.Stdout
。然後將切片複製到緩衝區io.Copy(buf, bytes.NewReader(slice))
,然後使用file.Seek(0, 0)
轉到文件原點,然後首先使用strings.NewReader
從字符串讀取,然後繼續使用io.MultiReader(r, file)
和bufio.NewScanner
讀取file
,然後使用fmt.Println(scanner.Text())
打印全部。
2 - 這是另一個有趣的使用界面:
package main
import "fmt"
func main() {
i := show()
fmt.Println(i) // 0
i = show(1, 2, "AB", 'c', 'd', []int{1, 2, 3}, [...]int{1, 2})
fmt.Println(i) // 7
}
func show(a ...interface{}) (count int) {
for _, b := range a {
if v, ok := b.(int); ok {
fmt.Println("int: ", v)
}
}
return len(a)
}
輸出:
0
int: 1
int: 2
7
而且很好的例子看看:Explain Type Assertions in Go
@nikoss我希望這可以幫助。 – 2016-08-23 10:43:02
接口提供了一些種類的泛型。想想鴨子打字。
type Reader interface{
Read()
}
func callRead(r Reader){
r.Read()
}
type A struct{
}
func(_ A)Read(){
}
type B struct{
}
func(_ B)Read(){
}
這是確定通過結構A
,並B
到callRead
,因爲都實現讀卡器接口。 但是如果沒有接口,我們應該爲A
和B
寫兩個函數。
func callRead(a A){
a.Read()
}
func callRead2(b B){
b.Read()
}
你將如何解構未知結構JSON?或者如果fmt.Printf不在那裏,它將如何工作? – YOU
好吧,它的工作如果不在那裏,我猜你是什麼意思如何工作?它是從fmt – nikoss
出口可能重複的[轉:界面的含義是什麼?](http://stackoverflow.com/questions/23148812/go-whats-the-meaning-of-interface) – molivier