Methods
Goには、クラス( class )のしくみはないが、型にメソッド( method )を定義できる。
struct宣言したTyoeにメソッドを追加することでクラス定義と似た振る舞いを記述できる。
type Vertex struct { X, Y float64 } // Vertex型にメソッドを定義 func (v Vertex) Abs() float64 { return math.Sqrt(v.X*v.X + v.Y*v.Y) } func main() { v := Vertex{3, 4} //定義したメソッドの呼び出し fmt.Println(v.Abs()) }
structに限らずメソッド定義が可能
type MyFloat float64 func (f MyFloat) Abs() float64 { if f < 0 { return float64(-f) } return float64(f) } func main() { f := MyFloat(-math.Sqrt2) fmt.Println(f.Abs()) }
Pointer receivers
定義したメソッドが参照できる定義元の変数は通常の関数引数同様、コピーして渡される。
よって他言語のオブジェクトのような振る舞いを期待するには、ポインタレシーバとして定義する。
type Vertex struct { X, Y float64 } func (v *Vertex) Scale(f float64) { v.X = v.X * f v.Y = v.Y * f } func main() { v := Vertex{3, 4} v.Scale(10) // ポインタレシーバーでない場合、vのメンバは変更されない fmt.Println(v) }
Methods and pointer indirection
ポインタレシーバを持つメソッドを変数から呼び出した場合、自動的にポインタレシーバーとして呼び出される。
var v Vertex v.Scale(5) // OK (&v).Scale(5)と等価 p := &v p.Scale(10) // OK
変数レシーバーの場合、ポインタを渡すと自動で参照先の値(*var)に置き換えられる。
var v Vertex fmt.Println(v.Abs()) // OK p := &v fmt.Println(p.Abs()) // OK (*p).Abs()と等価
Interfaces
Interfaces are implemented implicitly
インターフェースの実装にimplementsキーワードは必要ない。インターフェース型変数の初期化時にインターフェースに適合しているか判断している。
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() }
Type assertions
多分、他言語でいうダウンキャストのようなものかな?
var i interface{} = "hello" //string型の"hello"をsに代入 s := i.(string) fmt.Println(s) //okにはtrueが返される s, ok := i.(string) fmt.Println(s, ok) // 0, falseが返される f, ok := i.(float64) fmt.Println(f, ok)
Type switches
switchで型判定ができる。interfaceはタブルのような<value, type>を持つ構造なので、i.(type)
として、そのインターフェースの実装が何型であるかを取得できる。
func do(i interface{}) { switch v := i.(type) { case int: case string: default: } } func main() { do(21) do("hello") do(true) }
リンク
リンク