Golang接口
一、概念
接口类型是一种抽象的类型。它只展示出类型的方法,不做具体的实现。
//
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")
二、接口类型
接口类型是方法的集合,实现这些方法的具体类型是这个接口的实例。
- 空接口类型
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
- 简单实现
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)
}
}
- 请求多路器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)
}
- 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)是一个转换而非一个函数调用
- 全局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接口
- 定义
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 }
- 使用
调用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) 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
- 如果x == nil, 任何类型的T都会失败
- 类型判断
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...
}
- 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)
}
}
}
}