構造体は、データを構造化して利用するための方法です。構造化することでデータをグループ化することができます。
今回は、Golangにおける構造体の宣言方法と使用方法について見ていきます。
Golangで構造体を定義する方法
構造体を使用するには、使用する構造体の種類を宣言します。
以下のコードは、type キーワードを使用して構造体の型を定義する方法を示しています。
package main
import (
"fmt"
)
type Fruit struct {
name string
}
// type 構造体名 struct {
// field1 type1
// field2 type2
// ...
// }
func main() {
}
構造体の変数宣言
ここでは、構造体変数の宣言の仕方を見ていきます。以下のようにとても簡単です。
var 変数名 構造体名 // 構造体の変数を定義する
構造のゼロ値とは?
構造体が宣言されているが、値が割り当てられていない場合、その構造体はゼロ値の構造体となります。
つまり、すべてのフィールドは、それぞれの型からゼロ値をとります。
package main
import (
"fmt"
)
type Fruit struct {
name string
}
func main() {
var apple Fruit
fmt.Println(apple) // {}
}
GolangでのStructの作成と初期化
構造体を作成し、値で初期化する方法はいくつかあります。
構造体リテラル構文の使用
構造体リテラル構文は、宣言時に値を代入するだけなので、実に簡単です。
package main
import (
"fmt"
)
type Fruit struct {
name string
}
func main() {
var apple = Fruit{"Apple"} // 構造体リテラル構文
fmt.Println(apple) // {Apple}
}
new キーワードの使用
構造体を宣言するときにnewキーワードを使うことができます。
そして、ドット記法で値を代入して初期化することができます。
package main
import (
"fmt"
)
type Fruit struct {
name string
}
func main() {
var apple = new(Fruit)
apple.name = "Apple"
fmt.Println(apple) // &{Apple}
}
ポインタアドレス演算子の使用
ポインタアドレス演算子(&)を使って、構造体を宣言することができます。
その方法を見てみましょう。
package main
import (
"fmt"
)
type Fruit struct {
name string
}
func main() {
var mango = &Fruit{"Mango"}
fmt.Println(mango) // prints &{Mango}
}
Golangの構造体の種類
構造体は、名前を付けることもできますし、名前を付けないこともできます。どちらにも用途があります。
ここでは、構造体とは何か、どのように使うかを説明します。
名前付き構造体
名前付き構造体とは、以前に宣言された名前を持つ構造体のことです。
そのため、その名前を用いて構造体を初期化することができます。
type Food struct {} // Foodが構造体名
無名の構造体
次に、無名の構造体について説明します。
どのように作成し、どのように使用するか以下のコード例で見ていきます。
package main
import (
"fmt"
)
func main() {
pizza := struct {
name string
}{
name: "Pizza",
}
fmt.Println(pizza) // prints {Pizza}
}
構造体オブジェクトのフィールド
構造体は名前付き、もしくは無名のフィールドを持ちます。
また、フィールドには値を割り当てることもできますし、空のまま、つまりゼロ値を持つこともできます。
では、どのように初期化し、使用するかを以下のコード例で見てみましょう。
構造体の名前付きフィールド
名前付きフィールドとは、構造体型宣言で名前が宣言されているフィールドのことです。
以下はそのコード例です。
type Food struct {
name string // 名前を持つフィールド
quantity int // ...
}
構造体の無名フィールド
無名フィールドとは、型が宣言されているだけのフィールドのことです。
以下の例では、構造体の内部で任意の型を使用できることがわかりますが、これは曖昧さを生み、問題を引き起こす可能性があります。
package main
import (
"fmt"
)
type Painting struct {
string
Artist
}
type Artist struct {
string
}
func main() {
var p Painting
p.string = "Starry Night"
p.Artist.string = "ゴッホ"
fmt.Println(p) // prints {Starry Night {ゴッホ}}
}
フィールドとしての関数(function)
構造体は、そのフィールドとして関数を持つことができます。
以下は、構造体内部の関数のコード例です。
import (
"fmt"
)
// declare function type
type FoodNameGetter func(string) string
type Food struct {
name string
getter FoodNameGetter // declare function
}
func main() {
pizza := Food{
name: "Pizza",
getter: func(name string) string { // declare function body
return "This is " + name + "."
},
}
fmt.Println(pizza.getter(pizza.name)) // prints "This is Pizza."
}
構造体のフィールドにアクセスする方法
構造体のフィールドへのアクセスは実に簡単で、ドット演算子を使っています。
ネストした構造体がある場合は、ネストした構造体の内部でもドット「.」を使用します。
// declaration
type Car struct {
name string
cost int
}
var c Car = Car{"Mercedes", 3500000}
// accessing
carMake := c.name
carPrice := c.cost
ネストされた構造体の場合は、次のようになります。
package main
import (
"fmt"
)
type User struct {
name string
}
type Service struct {
name string
user User
}
func main() {
google := Service{
name: "Google",
user: User{
name: "John Doe",
},
}
// accessing from nested struct
fmt.Println(google.user.name) // prints "John Doe"
}
フィールドの推論
無名構造体を別の構造体の内部で使用すると、無名構造体のすべてのフィールドが外側の構造体の一部であるかのようにアクセスできるようになります。
このようなフィールドは、推論されたフィールドと呼ばれます。
package main
import (
"fmt"
)
type Person struct {
name string
}
type StoreKeeper struct {
Person
}
func main() {
var p = StoreKeeper{}
p.Person = Person{"Jane Doe"}
// StoreKeeperのフィールドとしてアクセスできる
fmt.Println(p.name) // prints "Jane Doe"
}
構造体のポインタを使用する
構造体のポインタは、アドレス演算子(&)を使って作成することができます。
package main
import (
"fmt"
)
type Student struct {
name string
}
func main() {
ptrStudent := &Student{name: "John"}
fmt.Println(ptrStudent) // &{John}
}
new( )関数を使えば、その構造体のポインタを取得することができます。
構造体の比較
構造体は、同じフィールドに同じ値があれば、比較することができます。以下はその例です。
package main
import (
"fmt"
)
type Student struct {
name string
}
func main() {
s1 := Student{"Tanaka"}
s2 := Student{"Tanaka"}
if(s1 == s2) {
fmt.Println("They are equal")
} else {
fmt.Println("They are not equal")
}
}
出力結果は以下の通りです。
They are equal
構造体の配列
構造体を配列で使用する方法はシンプルで、その使い方について説明します。
構造体配列の宣言と初期化
構造体配列の初期化は、その構造体の型を持つ配列を宣言するだけです。
var 構造体の配列 []構造体名
構造体配列への値の追加
構造体配列へ値を追加するには、挿入後の配列を返すappend関数を使用します。
package main
import (
"fmt"
)
type Student struct {
name string
}
func main() {
var students []Student // 配列の定義
s := Student{"Kate"}
kate := append(students, s)
fmt.Println(kate) // [{Kate}]
}
もしくは、単純に以下のようにインデックスを利用することもできます。
students[1] = Student{"Peter"}
このように、Goでは構造体を様々な用途に利用することができます。