本日のGo
忙しすぎて中々Goの勉強ができない。
interface
Javaで実装した時以来久しぶりに触る概念。
理解するのはとても大変そう。
package main import "fmt" type I interface { M() } type T struct { S string } func (t T) M() { fmt.Println(t.S) } func main() { var i I = T{"hello"} i.M() }
まずここではインターフェースを定義している。
また構造体のTに文字列helloを詰めいている。
var i I = T{"hello"}
M()によりインターフェースの構造体の中に入っているhelloを出力している。
i.M()
どんどんややこしくなってくる。
package main import ( "fmt" "math" ) type I interface { M() } type T struct { S string } func (t *T) M() { fmt.Println(t.S) } type F float64 func (f F) M() { fmt.Println(f) } func main() { var i I i = &T{"Hello"} describe(i) i.M() i = F(math.Pi) describe(i) i.M() } func describe(i I) { fmt.Printf("(%v,%T)\n", i, i) }
インターフェースを宣言したあとHelloが代入されている構造体をインタフェースに詰める。
i = &T{"Hello"}
describe(i)
i.M()
この関数をインターフェースに詰めている。
func (t *T) M() {
fmt.Println(t.S)
}
インターフェースを宣言したあとmath.Piが代入されている構造体をインタフェースに詰める。
i = F(math.Pi) describe(i) i.M()
この変数をインターフェースに詰めている。
type F float64
nilを出力させようとするとエラーになるのね。
package main import "fmt" type I interface { M() } func main() { var i I describe(i) i.M() } func describe(i I) { fmt.Printf("(%v, %T)\n", i, i) }
空のインターフェースが宣言できるらしい。
これによって色々な型を扱うことができるようになるらしい。便利。
package main import "fmt" func main() { var i interface{} describe(i) i = 42 describe(i) i = "hello" describe(i) } func describe(i interface{}) { fmt.Printf("(%v, %T)\n", i, i) }
インターフェースの値を利用するやり方。
インターフェースには"hello"(string型)を代入している。
package main import "fmt" func main() { var i interface{} = "hello" s := i.(string) fmt.Println(s) s, ok := i.(string) fmt.Println(s, ok) f, ok := i.(float64) fmt.Println(f, ok) f = i.(float64) fmt.Println(f) }
string型が代入されているからfloat64の型のコードをの存在している下記コード、float64型の値を呼び出そうとしている下記コードはfalseとエラーとなる。
f, ok := i.(float64) fmt.Println(f, ok) f = i.(float64) fmt.Println(f)
インタフェースの型によって分岐するswitch文が存在するのか。
go言語を学習して思うのはインタフェースを使う場面がめちゃくちゃ多いんだなと感じた。
package main import "fmt" func do(i interface{}) { switch v := i.(type) { case int: fmt.Printf("Twice %v is %v\n", v, v*2) case string: fmt.Printf("%q is %v bytes long\n", v, len(v)) default: fmt.Printf("I don't know about type %T!\n", v) } } func main() { do(21) do("hello") do(true) }
Stringerインタフェースを使うと変数を文字列で出力してくれるらしい。
package main import "fmt" type Person struct { Name string Age int } func (p Person) String() string { return fmt.Sprintf("%v (%v years)", p.Name, p.Age) } func main() { a := Person{"Donald", 42} z := Person{"Barak", 43} fmt.Println(a, z) }
ちょっとだけStringerの練習をしてみた。
[127 0 0 1]のように表示されるmap型をStringerで127.0.0.1と表示させるように変更してみた。
package main import "fmt" type IPAddr [4]byte func (ip IPAddr) String() string { return fmt.Sprintf("%v.%v.%v.%v", ip[0], ip[1], ip[2], ip[3]) } func main() { hosts := map[string]IPAddr{ "loopback": {127, 0, 0, 1}, "googleDNS": {8, 8, 8, 8}, } for name, ip := range hosts { fmt.Printf("%v:%v\n", name, ip) } }
errorインターフェース。
error時のログ取得に使えそう。
package main import ( "fmt" "time" ) type MyError struct { When time.Time What string } func (e *MyError) Error() string { return fmt.Sprintf("at %v,%s", e.When, e.What) } func run() error { return &MyError{ time.Now(), "it didn't work", } } func main() { if err := run(); err != nil { fmt.Println(err) } }