一、概念

接口类型是一种抽象的类型。它只展示出类型的方法,不做具体的实现。

	//
	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()
	}

	//io.Writer为接口类型
	package io
	// Writer is the interface that wraps the basic Write method.
	type Writer interface {
		Write(p []byte) (n int, err error)
	}

	//io.Writer实现示例
	type ByteCounter int
	func (c *ByteCounter) Write(p []byte) (int, error) {
		*c += ByteCounter(len(p)) // convert int to ByteCounter
		return len(p), nil
	}

	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")

二、接口类型

接口类型是方法的集合,实现这些方法的具体类型是这个接口的实例。

  1. 空接口类型

interface {} 可以接收任意值, 但是interface {} 没有方法,所以不能操作它所持有的值

	var any interface{}
	any = true
	any = 12.34
	any = "hello"
	any = map[string]int{"one": 1}
	any = new(bytes.Buffer)

三、接口值

接口由动态类型和动态值组成 接口的零值就是接口的类型和值都为nil,二者缺一不可 当动态值是可比较类型时, 接口值可以用 == 或 != 来比较

	//得知接口的动态值类型
	var w io.Writer
	fmt.Printf("%T\n", w) // "<nil>"
	w = os.Stdout
	fmt.Printf("%T\n", w) // "*os.File"
	w = new(bytes.Buffer)
	fmt.Printf("%T\n", w) // "*bytes.Buffer"

四、sort接口

	//原型
	package sort
	type Interface interface {
	Len() int
	Less(i, j int) bool // i, j are indices of sequence elements
	Swap(i, j int)

	//简单用法
	//对字符串切片进行排序
	type StringSlice []string
	func (p StringSlice) Len() int { return len(p) }
	func (p StringSlice) Less(i, j int) bool { return p[i] < p[j] }
	func (p StringSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }

	//调用
	names := []string{"Lily", "Lucy", "Jack", "Ada"}
	sort.Sort(StringSlice(names))

	//string排序已经内置到sort包中
	sort.Strings(names)
}

五、http.Handler接口

Handler接口的定义

	package http
	type Handler interface {
		ServeHTTP(w ResponseWriter, r *Request)
	}
	func ListenAndServe(address string, h Handler) error
  1. 简单实现
	func main() {
		db := database{"shoes": 50, "socks": 5}
		log.Fatal(http.ListenAndServe("192.168.0.4:8000", db))
	}

	type dollars float32
	type database map[string]dollars

	func (d dollars) String() string { return fmt.Sprintf("$%.2f", d) }

	func (db database) ServeHTTP(w http.ResponseWriter, req *http.Request) {
		for item, price := range db {
			fmt.Fprintf(w, "%s: %s\n", item, price)
		}
	}

  1. 请求多路器ServeMux

ServeMux可以简化URL和Handler的联系

	func main() {
		db := database{"shoes": 50, "socks": 10}
		mux := http.NewServeMux()
		mux.Handle("/list", http.HandlerFunc(db.list))
		mux.Handle("/price", http.HandlerFunc(db.price))
		log.Fatal(http.ListenAndServe("localhost:8000", mux))
	}

	type dollars float32

	func (d dollars) String() string {
		return fmt.Sprintf("$%.2f", d)
	}


	type database map[string]dollars

	func (db database) list(w http.ResponseWriter, req *http.Request) {
		for item, price := range db {
			fmt.Fprintf(w, "%s: %s\n", item, price)
		}
	}

	func (db database) price(w http.ResponseWriter, req *http.Request) {
		item := req.URL.Query().Get("item")
		price, ok := db[item]
		if !ok {
			w.WriteHeader(http.StatusNotFound) //404
			fmt.Fprintf(w, "no such item: %q\n", item)
			return
		}

		fmt.Fprintf(w, "%s\n", price)
	}

  1. HandlerFunc是一个类型,不是函数
	package http
	type HandlerFunc func(w ResponseWriter, r *Request)
	func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
		f(w, r)
	}

语句http.HandlerFunc(db.list)是一个转换而非一个函数调用

  1. 全局ServeMux
	func main() {
		db := database{"shoes": 50, "socks": 5}
		http.HandleFunc("/list", db.list)
		http.HandleFunc("/price", db.price)
		log.Fatal(http.ListenAndServe("localhost:8000", nil))
	}

六、error接口

  1. 定义
	type error interface {
		Error() string
	}

	package errors
	func New(text string) error { return &errorString{text} }
	type errorString struct { text string }
	func (e *errorString) Error() string { return e.text }
  1. 使用

调用errors.New函数,它会根据传入的错误信息返回一个新的error

	func main() {
		err := errors.New("I am a error!")
		if err != nil {
			fmt.Println(err.Error())
		}

		//fmt.Errorf更常用
		fmt.Println(fmt.Errorf("%s", "this is a errorf"))

		//syscall中的error
		var err error = syscall.Errno(2)
		fmt.Println(err.Error()) // "no such file or directory"
		fmt.Println(err) // "no such file or directory"
	}

七、类型断言

类型断言是一个使用在接口值上的操作。语法上它看起来像x.(T)被称为断言类型, 这里x表示一个接口的类型和T表示一个类型。一个类型断言检查它操作对象的动态 类型是否和断言的类型匹配。

  1. 分类
	//(1) T是具体类型
	var w io.Writer
	w = os.Stdout
	f := w.(*os.File) // success: f == os.Stdout
	c := w.(*bytes.Buffer) // panic: interface holds *os.File, not *bytes.Buffer

	//(2) T是接口类型
	var w io.Writer
	w = os.Stdout
	rw := w.(io.ReadWriter) // success: *os.File has both Read and Write
	w = new(ByteCounter)
	rw = w.(io.ReadWriter) // panic: *ByteCounter has no Read method
  1. 如果x == nil, 任何类型的T都会失败
  2. 类型判断
	var w io.Writer = os.Stdout
	f, ok := w.(*os.File) // success: ok, f == os.Stdout
	b, ok := w.(*bytes.Buffer) // failure: !ok, b == nil

	if f, ok := w.(*os.File); ok {
	// ...use f...
	}
  1. XML解码
func main() {
    dec := xml.NewDecoder(os.Stdin)
    var stack []string
    for {
        tok, err := dec.Token()
        if err == io.EOF {
            break 
        } else if err != nil {
            fmt.Fprintf(os.Stderr, "%v\n", err)
            os.Exit(1)
        }   
        
        switch tok := tok.(type) {
        case xml.StartElement:
            stack = append(stack, tok.Name.Local)
        case xml.EndElement:
            stack = stack[:len(stack)-1]
        case xml.CharData:
            if containsAll(stack, os.Args[1:]) {
                fmt.Printf("%s: %s\n", strings.Join(stack, " "), tok)
            }   
        }   
    }   
}