1.接口interface
1)接口是一個或多個方法簽名的集合
2)只要某個類型擁有該接口的所有方法簽名,即算實現該接口無需顯示聲明實現了哪個接口,這稱爲Structural Typing
3)接口只有方法聲明,沒有實現,沒有數據字段
4)接口可以匿名嵌入其他接口,或嵌入到結構中
5)將對象賦值給接口時,會發生拷貝,而接口內部存儲的是指向這個複製品的指針,既無法修改複製品的狀態,也無法獲取指針
6)只有當接口存儲的類型和對象爲nil時,接口才等於nil
7)接口調用不會做receiver的自動轉換
8)接口統一支持匿名自動方法
9)接口也可以實現類似OOP中的多態
10)空接口可以作爲任何類型數據的容器
11)接口是由使用者定義得
2.類型斷言
1)通過類型斷言的ok pattern可以判斷接口中的數據類型
2)使用tupe switch 則可針對空接口進行比較全面的類型判斷
3.接口轉換
可以將擁有超集的接口轉換爲子集的接口
實例,使用者定義接口
創建一個retiever目錄,裏面包含main.go 以及csdn/csdnRetiever.go兩個文件
main.go代碼,這裏面使用到了定義的csdn這個包調用接口Retiever的Get方法:
package main
import (
"fmt"
"my-code/test-code/retiever/csdn"
)
type Retiever interface {
Get(url string) string
}
func download(r Retiever) string {
return r.Get("www.csdn.com")
}
func main() {
var r Retiever
r = csdn.Retiever{"hello www.csdn.com"}
fmt.Println(download(r))
}
csdn/csdnRetiever.go代碼中包含接口定義:
package csdn
type Retiever struct {
Contents string
}
func (r Retiever) Get(url string) string {
return r.Contents
}
上述代碼傳入一個url字符串並直接return
輸出:
API server listening at: 127.0.0.1:34276
hello www.csdn.com
實例,訪問百度
創建一個retiever目錄,裏面包含main.go 以及baidu/baidu.go兩個文件
main.go代碼,這裏面使用到了定義的baidu這個包調用接口Retiever的Get方法:
package main
import (
"fmt"
"my-code/test-code/retiever/baidu"
)
type Retiever interface {
Get(url string) string
}
func download(r Retiever) string {
return r.Get("http://www.baidu.com")
}
func main() {
var r Retiever
r = baidu.Retiever{}
fmt.Println(download(r))
}
baidu/baidu.go代碼直接調用內置http接口訪問百度:
package baidu
import (
"net/http"
"net/http/httputil"
"time"
)
type Retiever struct {
UserAgent string
TimeOut time.Duration
}
func (r Retiever) Get(url string) string {
resp, err := http.Get(url)
if err != nil {
panic(err)
}
result, err := httputil.DumpResponse(resp, true)
resp.Body.Close()
if err != nil {
panic(err)
}
return string(result)
}
上述實例運行結果是獲取到了百度首頁的html源碼
4.查看接口內部元素
接口內部究竟是什麼內容呢,將上述main.go代碼修改如下打印出來查看:
func main() {
var r Retiever
r = baidu.Retiever{}
//fmt.Println(download(r))
fmt.Printf("%T\n%v\n", r, r)
}
運行結果如下:
API server listening at: 127.0.0.1:18265
baidu.Retiever
{ 0s}
上述打印看出%T打印的內容是baidu.Retiever這是接口名稱,%v的內容是{ 0s},這是因爲裏面兩個元素UserAgent未賦值因此是個空格,TimeOut未賦值所以默認0秒。爲了更清晰的看出效果,下面將上述元素賦值查看:
package main
import (
"fmt"
"my-code/retiever/baidu"
"time"
)
type Retiever interface {
Get(url string) string
}
func download(r Retiever) string {
return r.Get("http://www.baidu.com")
}
func main() {
var r Retiever
r = baidu.Retiever{
UserAgent: "Mozilla/5.0",
TimeOut: time.Minute,
}
//fmt.Println(download(r))
fmt.Printf("%T\n%v\n", r, r)
}
運行效果如下:
API server listening at: 127.0.0.1:16843
baidu.Retiever
{Mozilla/5.0 1m0s}
可以明顯看出被賦值了
5.指針類型
如果接口類型修改爲指針類型呢,代碼如下:
baidu/baidu.go
package baidu
import (
"net/http"
"net/http/httputil"
"time"
)
type Retiever struct {
UserAgent string
TimeOut time.Duration
}
func (r *Retiever) Get(url string) string {
resp, err := http.Get(url)
if err != nil {
panic(err)
}
result, err := httputil.DumpResponse(resp, true)
resp.Body.Close()
if err != nil {
panic(err)
}
return string(result)
}
main.go源碼不變:
package main
import (
"fmt"
"my-code/retiever/baidu"
"time"
)
type Retiever interface {
Get(url string) string
}
func download(r Retiever) string {
return r.Get("http://www.baidu.com")
}
func main() {
var r Retiever
r = baidu.Retiever{
UserAgent: "Mozilla/5.0",
TimeOut: time.Minute,
}
//fmt.Println(download(r))
fmt.Printf("%T\n%v\n", r, r)
}
那麼上述就會出錯,執行報錯如下:
# my-code/retiever
.\main.go:19:4: cannot use baidu.Retiever literal (type baidu.Retiever) as type Retiever in assignment:
baidu.Retiever does not implement Retiever (Get method has pointer receiver)
exit status 2
Process exiting with code: 1
此時就要修改爲取地址了:
主函數修改如下:
func main() {
var r Retiever
r = &baidu.Retiever{
UserAgent: "Mozilla/5.0",
TimeOut: time.Minute,
}
//fmt.Println(download(r))
fmt.Printf("%T\n%v\n", r, r)
}
這樣執行就不會出錯了。
Type assertion
通過指針類型獲取內部元素:
val := r.(*baidu.Retiever)
fmt.Println(val.TimeOut)
這樣就可以取得具體元素了。
6.接口的組合
baidu/baidu.go提供Retiever接口:
package baidu
type Retiever struct {
Contents string
}
func (r *Retiever) Post(url string, form map[string]string) string {
r.Contents = form["contents"]
return "ok"
}
func (r *Retiever) Get(url string) string {
return r.Contents
}
main.go代碼這裏面使用Poster和Retiever兩個接口組合成一個RetieverPoster接口
package main
import (
"fmt"
"my-code/retiever/baidu"
)
type Retiever interface {
Get(url string) string
}
type Poster interface {
Post(url string, form map[string]string) string
}
const url = "http://www.baidu.com"
func download(r Retiever) string {
return r.Get(url)
}
func post(poster Poster) {
poster.Post(url,
map[string]string{
"name": "baidu",
"course": "golang",
})
}
type RetieverPoster interface {
Retiever
Poster
}
func session(s RetieverPoster) string {
s.Post(url, map[string]string{
"contents": "hello baidu",
})
return s.Get(url)
}
func main() {
retriever := baidu.Retiever{"hello http://www.baidu.com"}
fmt.Println(session(&retriever))
}
執行效果如下:
API server listening at: 127.0.0.1:41871
hello baidu
雖然傳入的是"hello http://www.baidu.com"但是輸出的卻是"hello baidu"這是因爲在session方法裏面修改了內容導致的。
7.GO語言常用系統接口
Stringer接口,在fmt/print.go內:
type Stringer interface {
String() string
}
應用:在baidu包內實現一個String(),代碼如下:
baidu/baidu.go
package baidu
import "fmt"
type Retiever struct {
Contents string
}
func (r *Retiever) String() string {
return fmt.Sprintf(
"Retriver:{Contents=%s}", r.Contents)
}
func (r *Retiever) Post(url string, form map[string]string) string {
r.Contents = form["contents"]
return "ok"
}
func (r *Retiever) Get(url string) string {
return r.Contents
}
main.go
package main
import (
"fmt"
"my-code/retiever/baidu"
)
type Retiever interface {
Get(url string) string
}
func main() {
r := &baidu.Retiever{
Contents: "hello word",
}
fmt.Printf("%T\n%v\n", r, r)
}
輸出:
API server listening at: 127.0.0.1:39863
*baidu.Retiever
Retriver:{Contents=hello word}
此外還有兩個接口在io.go包內,分別是Reader和Writer
type Reader interface {
Read(p []byte) (n int, err error)
}
type Writer interface {
Write(p []byte) (n int, err error)
}
實例:
package main
import (
"bufio"
"fmt"
"io"
"os"
"strings"
)
func printFile(name string) {
file, err := os.Open(name)
if err != nil {
panic(err)
}
testIoReader(file)
}
func testIoReader(reader io.Reader) {
scanner := bufio.NewScanner(reader)
for scanner.Scan() {
fmt.Println(scanner.Text())
}
}
func main() {
printFile("ioTest.go")
fmt.Println("##############print string#################")
s := `abc"d"
beijing
nihao
hello`
testIoReader(strings.NewReader(s))
}
可以打印文件內容或者字符串裏面的內容。
小結:
1.接口變量裏面有什麼
1)接口變量自帶指針
2)接口變量同樣採用值傳遞,幾乎不需要使用接口的指針
3)指針接收者實現只能以指針方式使用;值接受者都可
2.查看接口變量
1)表示任何類型:interface{}
2)Type Assertion
3)Type Switch