2章ではstringやintなど,
type
次のような,
func ProcessTask(id, priority int) {
}
この関数を呼び出す側は次のようになります。
id := 3 // int
priority := 5 // int
ProcessTask(id, priority)
正しく呼び出せていますが,
id := 3
priority := 5
ProcessTask(priority, id) // コンパイルは通る
引数の型が合っているため,
このような場合,type
を用いて既存の型を拡張した独自の型を定義できます。
type ID int
type Priority int
func ProcessTask(id ID, priority Priority) {
}
type
のあとには,
呼び出す際には,
var id ID = 3
var priority Priority = 5
ProcessTask(priority, id) // コンパイルエラー
このように適切な型を用意することで,
構造体 (struct)
Goには,
構造体型の宣言
ここでは,id,
done
Task
という型を定義してみます。
type Task struct {
ID int
Detail string
done bool
}
構造体型もtype
を用いて宣言し,
この型から値を生成するには,
func main() {
var task Task = Task{
ID: 1,
Detail: "buy the milk",
done: true,
}
fmt.Println(task.ID) // 1
fmt.Println(task.Detail) // "buy the milk"
fmt.Println(task.done) // true
}
変数task
には,
構造体に定義した順でパラメータを渡すことで,
func main() {
var task Task = Task{
1, "buy the milk", true,
}
fmt.Println(task.ID) // 1
fmt.Println(task.Detail) // "buy the milk"
fmt.Println(task.done) // true
}
構造体の生成時に値を明示的に指定しなかった場合は,
func main() {
task := Task{}
fmt.Println(task.ID) // 0
fmt.Println(task.Detail) // ""
fmt.Println(task.done) // false
}
ポインタ型
構造体型もアドレスを取得し,&
を付けると,Task
のポインタ型は*Task
という型になります。
var task Task = Task{} // Task型
var task *Task = &Task{} // Taskのポインタ型
たとえば,
type Task struct {
ID int
Detail string
done bool
}
func Finish(task Task) {
task.done = true
}
func main() {
task := Task{done: false}
Finish(task)
fmt.Println(task.done) // falseのまま
}
この関数の引数をポインタ型にするには,*Task
とします。ポインタを渡すことで,
func Finish(task *Task) {
task.done = true
}
func main() {
task := &Task{done: false}
Finish(task)
fmt.Println(task.done) // true
}
このように,
new()
構造体は,new()
を用いて初期化することもできます。new()
は,
type Task struct {
ID int
Detail string
done bool
}
func main() {
var task *Task = new(Task)
task.ID = 1
task.Detail = "buy the milk"
fmt.Println(task.done) // false
}
コンストラクタ
Goには構造体のコンストラクタにあたる構文がありません。代わりにNew
で始まる関数を定義し,Task
をNew
する関数はNewTask()
という関数にし,Task
を生成し,
func NewTask(id int, detail string) *Task {
task := &Task{
ID: id,
Detail: detail,
done: false,
}
return task
}
func main() {
task := NewTask(1, "buy the milk")
// &{ID:1 Detail:buy the milk done:false}
fmt.Printf("%+v", task)
}
メソッド
型にはメソッドを定義できます。メソッドは,Task
の文字列表現を返すString()
というメソッドをTask
に定義してみます。
func (task Task) String() string {
str := fmt.Sprintf("%d) %s", task.ID, task.Detail)
return str
}
func main() {
task := NewTask(1, "buy the milk")
fmt.Printf(“%s”, task) // 1) buy the milk
}
このメソッドは,Task
のコピーがレシーバとして渡されるため,
呼び出し側に変更を反映したい場合は,Finish()
というメソッドをTask
に定義してみます。
func (task *Task) Finish() {
task.done = true
}
func main() {
task := NewTask(1, "buy the milk")
task.Finish()
// &{ID:1 Detail:buy the milk done:true}
fmt.Printf("%+v", task)
}
今回は,Finish()
が実行されたTask
のポインタを受け取り,