[Go言語] Goでのキャスト (型アサーション)

Go言語では、インターフェース型xから型Tについて、

x.(T)

のような記法を型アサーションと呼びます。 Tがインターフェース型の場合、動的な型xがインターフェースTを実装しているかどうかを確認します。

xがTを実装しているかどうかは実行時に決まるため、失敗する場合があります。 いわゆるダウンキャストに相当する操作をすることができるため、C++のdynamic_castや、WindowsのCOMで言うところのQueryInterfaceに近いイメージだと思います。

型アサーションが以下のように代入や初期化で使われると、(T, bool)のペアを返します。

v, ok := x.(T)

次の例では、FooableとBarableを実装した”Hogus”と、Fooableのみ実装した”Bogus”を作成して、型アサーションによってHogusがBarableにキャストできてBogusができないことを確認しています。

http://play.golang.org/p/GggE7R1yw2

package main

import "fmt"

type Fooable interface {
    Foo()
}

type Barable interface {
    Bar()
}

type Hocus struct {
    format string
}

func NewHocus() (*Hocus) {
    b := new(Hocus)
    b.format = "-- %s\n"
    return b
}

func (self *Hocus) Foo() {
    fmt.Printf(self.format, "Hocus Foo")
}

func (self *Hocus) Bar() {
    fmt.Printf(self.format, "Hocus Bar")
}

type Bogus struct {
    format string
}

func NewBogus() (*Bogus) {
    b := new(Bogus)
    b.format = "== %s\n"
    return b
}

func (self *Bogus) Foo() {
    fmt.Printf(self.format, "Bogus Foo")
}

func main() {
    testHocus()
    testBogus()
}

func testHocus() {
    var o Fooable
    o = NewHocus()
    o.Foo()
    p, ok := o.(Barable)
    p.Bar()
    fmt.Printf("Type assertion result is %t\n", ok)
}

func testBogus() {
    var o Fooable
    o = NewBogus()
    o.Foo()
    p, ok := o.(Barable)
    fmt.Printf("Bogus Barable is %T\n", p)
    fmt.Printf("Type assertion result is %t\n", ok)
}

参考

  • http://golang.org/ref/spec#Type_assertions
  • http://ja.wikipedia.org/wiki/%E5%9E%8B%E5%A4%89%E6%8F%9B

[Go言語] コンストラクタの書き方

Go言語のコンストラクタは以下のように書くことが推奨されています。

http://play.golang.org/p/oM5C26l8iw

package main

import "fmt"

type Person struct {
    Name string
    Age int
}

func NewPerson (name string, age int) *Person {
    if age < 0 {
        return nil
    }
    p := new(Person)
    p.Name = name
    p.Age = age
    return p
}

func main() {
    taro := NewPerson("Taro", 26)
    fmt.Printf("name: %s age: %d", taro.Name, taro.Age)
}

NewPersonは構造体の初期化を使って以下のように記述することもできます。

func NewPerson (name string, age int) *Person {
    if age < 0 {
        return nil
    }
    p := Person{name, age}
    return &p
}

Cに慣れた人にしてみるとローカル変数のポインタを返すことに違和感があるかもしれませんが、メモリ領域は呼ぶ度に新たに確保されます。

  • Effective Go – Constructors and composite literals
    http://golang.org/doc/effective_go.html#composite_literals

[Go言語] アノニマスフィールド(anonymous field)による継承とオーバーライド

Go言語には継承はありませんが、アノニマスフィールド(anonymous field)を使うことで共通のフィールドとメソッドを持つ構造体(struct)を作ることができます。詳しくはドキュメント等を参照して下さい。

今回は、アノニマスフィールドで継承した場合のオーバーライドの扱いを調べました。インターフェース渡しのメソッドでは元の構造体のメソッドが呼ばれます。ちょっと例が長いですが。

インターフェース渡しの例

http://play.golang.org/p/ebyaLR1ruQ

package main

import "fmt"

type Animal interface {
    Eat()
}

type Mammal struct {
    Weight int
}

type Human struct {
    Mammal
}

func (a *Mammal) Eat() {
    a.Weight += 11
}

func (h *Human) Eat() {
    h.Weight += 23
}

func main() {
    // Human
    h := &Human{Mammal{1}}

    serveFood(h)

    fmt.Printf("Human weight is %d\n", h.Weight)

    // Mammal
    m := &Mammal{1}

    serveFood(m)

    fmt.Printf("Mammal weight is %d\n", m.Weight)
}

func serveFood(a Animal) {
    a.Eat()
}

実行結果

Human weight is 24
Mammal weight is 12
Program exited.

一方、メソッドの引数にポインタを渡すとMammalのメソッドが呼ばれてしまいます。

ポインタ渡しの例

http://play.golang.org/p/ZEiHehkhID

package main

import "fmt"

type Mammal struct {
    Weight int
}

type Human struct {
    Mammal
}

func (a *Mammal) Eat() {
    a.Weight += 11
}

func (h *Human) Eat() {
    h.Weight += 23
}

func main() {
    // Human
    h := &Human{Mammal{1}}

    serveFood(&h.Mammal)

    fmt.Printf("Human weight is %d\n", h.Weight)
}

func serveFood(a *Mammal) {
    a.Eat()
}

実行結果

Human weight is 12
Program exited.

アノニマスフィールドのポインタを使うのは避けた方が良さそうです。

余談ですが、Goにはオーバーロードはありません。
http://golang.org/doc/faq#overloading

参考

[Go言語] インターフェース引数は値渡しなのか参照渡しなのか

Go言語(golang)にはポインタとインターフェースがありますが、「インターフェースへのポインタ」で渡す必要があるのかどうか。

結論から言うと、インターフェースはインスタンスへの参照を持っているのでインターフェースへのポインタを渡す必要はありません。
以下の例では、serveFoodメソッドにインターフェースで渡していますが、元のインスタンスが変更されていることが分かります。

http://play.golang.org/p/M6is2Gpe0B

package main

import "fmt"

type Animal interface {
    Eat()
}

type Human struct {
    Vital string
}

func (h *Human) Eat() {
    h.Vital = "full"
}

func main() {
    a := &Human{"empty"}

    serveFood(a)

    fmt.Printf("vital is %s", a.Vital)
}

func serveFood(a Animal) {
    a.Eat()
}

実行結果

vital is full
Program exited.

まあ、インターフェースの値って何なのか考えてみれば明らかだと思いますが、ポインタに不慣れな人には理解が難しいところかもしれません。

参考