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() 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)
}
複製程式碼
&{1 王小明 18}
複製程式碼
改成:
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)
}
複製程式碼
學號: 1
真實姓名: 王小明
年齡: 18
複製程式碼
瞬間感覺高大上了吧!!
Error
type TestError struct {}
func (t TestError) Error() string {
return "我是Error"
}
func main() {
fmt.Println(TestString{})
}
複製程式碼
我是Error
複製程式碼
實際上使用方式跟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)
}
複製程式碼
我是Format
我是c
我是+c
我是Format
複製程式碼
fmt.Println
也會呼叫Format的介面,所以String()
Format()
不能同一個結構體裡面。 通常使用跟Error()
類似,可以參考一下github.com/pkg/errors裡的stack.go
的func (f Frame) Format(s fmt.State, verb rune)
GoString
type TestGoString struct {}
func (t TestGoString) GoString() string {
return "我是GoString"
}
func main() {
t := TestGoString{}
fmt.Println(TestGoString{})
fmt.Printf("%s %#v\n", t, t)
}
複製程式碼
{}
{} 我是GoString
複製程式碼
如上所示fmt.Println
並沒呼叫GoString方法,只能通過格式化%#
+標記輸出。
在沒有實現介面的情況下,通常用來輸出預設相應值,如下:
func main() {
var i uint = 18
// 輸出十六進位制
fmt.Printf("%x\n", i)
fmt.Printf("%#x\n", i)
}
複製程式碼
12
0x12
複製程式碼
注意事項
fmt/print.go
的pp.handleMethods(verb rune) (handled bool)
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方法呼叫控制,都不是互相獨立,根據優先順序呼叫。所以介面的設計,儘可能獨立封裝,避免混淆。
小結
String()
用於對結構體的標準輸出等。
Error()
封裝error的方法,可以改一些錯誤上傳到日誌系統或者列印Stack。
Format()
對於String()
的高階用法,用於多種型別或者格式使用。
GoString()
常用於相對值。
歡迎大神們交流,指導!