急いで学ぶGo lang#5 構造体

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

構造体を使いましょう

C言語にもある機能で、いろいろあるデータをまとめて1つのまとまりにしたものです。
JavaScriptでいうオブジェクト、Javaでいうクラスのようなものです。
Goの場合はC言語の構造体よりもう少し便利な機能もあるので、そのへんもふまえて解説します。

動作環境

今回使用した動作環境は以下のとおりです。

  • OS : MacOS X 10.9.4
  • Go : 1.4.1
  • IDE : IntelliJ IDEA 14 CE

ここを参考に、GOPATHとGOROOTは設定しておいてください。

構造体の基礎

Goの構造体はC言語の構造体とだいたい同じですが、
・メソッドの定義
・構造体埋め込み
等の機能も持っています。 Goはクラスや継承の機能がサポートされていませんが、上記機能 + αを使用することでオブジェクト指向プログラミングが可能になります。

構造体の定義と初期化

構造体は下記のようにstructを使用して定義します。
Goの場合、大文字から始まる名前 (関数名、型名、フィールド名) は、他のパッケージからアクセス可能となります。
(Javaでいうpublic的な)
小文字で始まる名前の場合、他のパッケージからアクセス不可能です。(Javaでいうパッケージデフォルト的な)
同じパッケージ内であれば小文字でもアクセス可能です。

//User構造体定義。大文字から始まっているので他パッケージからアクセス可能
type User struct {
  name string
  age  int
}

//User構造体にアクセス
var u User
u.name = "taro"              
u.age = 30                   
fmt.Printf("name:%s", u.name)

構造体の初期化方法は複数あります。
下記のように変数定義後にフィールドを設定する方法、{}で順番にフィールドの値を渡す方法、
あとはフィールド名を:で指定する方法があります。

//宣言後にフィールド設定
var u User
u.name = "taro"
u.age = 30
fmt.Printf("name:%\n", u.name)

//順番で初期化
u2 := User{"taro", 30}
fmt.Println("name:%s", u2.name)

//Key:Valueで初期化。
u3 := User{name: "taro", age: 30}
fmt.Println("name:%s", u3.name)

構造体に&(アドレス演算子)を使って初期化したり、newキーワードを使用するとポインタ型で受け取ることができます。

//Key:Valueで初期化。u4はポインタ型
u4 := &User{name: "taro", age: 30} // var u4 *User
fmt.Println("name:%s", u4.name)

//newで初期化.u5はポインタ型
u5 := new(User) // var u5 *User
u5.name = "taro"
fmt.Println("name:%s", u5.name)

ちなみにGoだと、構造体は下記のような初期化関数を作るのが一般的らしいです。

func newUser(name string, age int) *User {
    u := new(User)
    u.name = name
    u.age = age
    return u
}

var user *User = newUser("taro",30)

ネスト構造体

構造体はネストさせることができます。下記サンプルは、Foo構造体がBar構造体を含んでいる形になります。

type Foo struct {
	Bar
	name    string
	fooProp string
}
type Bar struct {
	barProp string
}

foo := Foo{Bar{"barProp"}, "foo","fooProp"}
fmt.Println(foo.name) // foo
fmt.Println(foo.barProp) //barProp
fmt.Println(foo.Bar.barProp) //barProp

FooはBarの持つフィールドを継承しているので、「foo.barProp」のように、直接アクセスすることができます。
また、「foo.Bar.barProp」のように、たどっていくことも可能です。
もしFooとBarが同じnameというフィールドを持っていた場合、Barのnameへは「foo.Bar.name」とアクセスしなければなりません。

メソッド定義

Goの構造体にはもう1つ便利な機能があります。それがメソッドです。
Javaにおいて作成したクラスに関数を定義できるように、Goでは任意の型(レシーバ)と関数を関連付けることが可能です。
下記構文でメソッドを定義することができます。

func (レシーバ名  レシーバの型) 関数名(引数) (戻り値)

では、さきほどのUser構造体にメソッドを定義して使ってみましょう。
あいさつ文字列を返すgreetというメソッドを定義してみます。

type User struct {
  name string
  age  int
}

func (u User) greet() string {
 return "hello," + u.name;
} 

u := User {"taro",30}
fmt.Println(u.greet())

greetメソッドではレシーバーにUserを指定しています。メソッドはフィールドと同じように、「.」 を使ってアクセスします。
メソッドではレシーバ(User)のもつフィールドにアクセスすることが可能です。
また、同じ名前のメソッドがあっても、レシーバが異なればメソッドは別のものとして扱われるので注意してください。
なお、上記例では構造体に対してメソッドを定義していますが、構造体に対してしかメソッドを定義できないわけではありません。
既存の型や独自定義の型など、いろいろな型でメソッドを定義することが可能です。

参考サイトなど