A Tour of Go - Method and interfaces 自分用リファレンスメモ

個人開発したアプリの宣伝
目的地が設定できる手帳のような使い心地のTODOアプリを公開しています。
Todo with Location

Todo with Location

  • Yoshiko Ichikawa
  • Productivity
  • Free

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)
}