Go にはクラスがありませんが、struct type に method を設定することで同じようなことができます。method とは receiver を持つ function のことです。
receiver には2つの種類があり、1つは value, そして pointer です。
package main
import (
"fmt"
)
type document struct {
title string
author string
}
func (d document) display() {
fmt.Printf("Title: %s, Author: %s\n",
d.title,
d.author,
)
}
func (d *document) setTitle(title string) {
d.title = title
}
func main() {
d1 := &document{"A project report", "John"}
d1.display()
d2 := document{"A sales report", "Paul"}
d2.setTitle("A business report")
d2.display()
}
上記の場合、display() は value receiver, setTitle() は pointer receiver を持ちます。
ここで document の instance が value / pointer に関わらず、2つの method が使えているように見えるのは、Go が method の receiver に合うようにしてくれるからです。
例えば d1.display() は (*d1).display() のように呼び出してくれます。
また、d2.setTitle() は (&d2).setTitle() といった具合です。
しかし、interface を使用する場合には注意が必要です。
package main
import (
"fmt"
)
type displayer interface {
display()
}
type document struct {
title string
author string
}
func (d *document) display() {
fmt.Printf("Title: %s, Author: %s\n",
d.title,
d.author,
)
}
func displayDocument(d displayer) {
d.display()
}
func main() {
d := document{"A project report", "John"}
displayDocument(d)
}
$ go build main.go
./main.go:29: cannot use d (type document) as type displayer in argument to displayDocument:
document does not implement displayer (display method has pointer receiver)
この場合、コンパイルがエラーになってしまいます。
d := document{"A project report", "John"}
display() が pointer receiver なのに、d が value instance だからです。
Go の
ドキュメントには以下のようにあります。
The method set of any other type T consists of all methods declared with receiver type T. The method
set of the corresponding pointer type *T is the set of all methods declared with receiver *T or T
どの type T の method set も receiver type T で宣言された method で構成されます。
pointer type *T に対応する method set は *T もしくは T で宣言された method set です。
分かりづらいですが、value の method set は value receiver のみという制限があるんですね。pointer の method set には value receiver, pointer receiver どちらも使用できます。
d := &document{"A project report", "John"}
このように修正すると、コンパイルが通るようになります。
なぜ、このような制限があるのかというと、アドレスが常に取得できるわけではないからです。
package main
import (
"fmt"
)
type price int
func (p *price) appendYen() string {
return fmt.Sprintf("\u00A5%d", *p)
}
func main() {
fmt.Println(price(1000).appendYen())
}
$ go build main.go
./main.go:14: cannot call pointer method on price(1000)
./main.go:14: cannot take the address of price(1000)