golang fmt 包String(),Error(),Format(),GoString()的接口实现

Golang 归档:201901
普通
浏览:138
2019-01-12 22:19:32
golang fmt 包String(),Error(),Format(),GoString()的接口实现

golang的接口使用非常广泛,几乎每一个包都会用到接口,fmt包的使用率最多之一。在实际开发中,要定义结构体的标准输出用String(),定义标准错误输出Error(),定义格式化输出Format(),还有比较特殊的GoString()。接下来描述接口的使用方式,使用场景,还有注意的地方。

String()

type TestString struct {}
func (t TestString) String() string {
    return "我是String"
}
func main() {
    fmt.Println(TestString{})
}

使用起来比较简单,只要结构体里面有String() string就可以输出。
fmt包里面会判断有没有fmt.Stringer的接口,然后再调用。
通常用于结构体的默认输出,例如:

type Student struct {
    number int
    realname string
    age int
}
func main() {
    stu := &Student{
        number: 1,
        realname: "王小明",
        age: 18,
    }
    fmt.Println(stu)
}

改成:

type Student struct {
    number int
    realname string
    age int
}
func (t *Student) String() string {
    return fmt.Sprintf("学号: %d\n真实姓名: %s\n年龄: %d\n", t.number, t.realname, t.age)
}
func main() {
    stu := &Student{
        number: 1,
        realname: "王小明",
        age: 18,
    }
    fmt.Println(stu)
}

Error

type TestError struct {}
func (t TestError) Error() string {
    return "我是Error"
}
func main() {
    fmt.Println(TestString{})
}

实际上使用方式跟String()一样,但是设计代码时不能互相替换实现。
最常用的用法是独立封装type XXXError struct{},在文章最尾会揣摸一下为什么要这样用。

Format

type TestFormat struct {}
func (t TestFormat) Format(s fmt.State, c rune) {
    switch c {
    case 'c':
        switch {
        case s.Flag('+'):
            fmt.Printf("我是+c\n")
        default:
            fmt.Fprint(s, "我是c\n")
        }
    default:
        fmt.Print("我是Format")
    }
}
func main() {
    t := TestFormat{}
    fmt.Println(t)
    fmt.Printf("%c\n", t)
    fmt.Printf("%+c\n", t)
    fmt.Printf("%s\n", t)
}

fmt.Println也会调用Format的接口,所以String() Format()不能同一个结构体里面。 通常使用跟Error()类似,可以参考一下github.com/pkg/ errors里的stack.go的func (f Frame) Format(s fmt.State, verb rune)

GoString

ype TestGoString struct {}
func (t TestGoString) GoString() string {
    return "我是GoString"
}
func main() {
    t := TestGoString{}
    fmt.Println(TestGoString{})
    fmt.Printf("%s %#v\n", t, t)
}

如上所示fmt.Println并没调用GoString方法,只能通过格式化%#+标记输出。
在没有实现接口的情况下,通常用来输出默认相应值,如下:

func main() {
    var i uint = 18
    // 输出十六进制
    fmt.Printf("%x\n", i)
    fmt.Printf("%#x\n", i)
}

注意事项

func (p *pp) handleMethods(verb rune) (handled bool) {
    ...
    // 判断Formatter
    if formatter, ok := p.arg.(Formatter); ok {
        ...
        formatter.Format(p, verb)
        return
    }

        // 判断是否含有#标识符
    if p.fmt.sharpV {
            // 判断GoStriner
        if stringer, ok := p.arg.(GoStringer); ok {
                        ...
            p.fmt.fmtS(stringer.GoString())
            return
        }
    } else {
        switch verb {
        case 'v', 's', 'x', 'X', 'q':
            switch v := p.arg.(type) {
            // 符合error接口
            case error:
                                ...
                p.fmtString(v.Error(), verb)
                return
                        // 符合Stringer接口
            case Stringer:
                                ...
                p.fmtString(v.String(), verb)
                return
            }
        }
    }
    return false
}

Format -> (#)GoString -> ((v,s,x,X,q)Error -> String) 源码四个接口都在handlerMethods方法调用控制,都不是互相独立,根据优先顺序调用。所以接口的设计,尽可能独立封装,避免混淆。

注意事项
  • 本文地址:https://22v.net/article/11/
  • 转载本文时,请注明转载自“SamBlog”的字样。
  • 如此文章有损您的合法权益,请使用页面底部的邮箱与我取得联系。
分类目录
文章归档
友情站点