goはオブジェクト指向言語とは違って、書いていてC言語を書いている印象に近く、OOPで言うところのクラスが構造体で、クラスにメソッドを定義することは、構造体にメソッドを定義することになる。
しかし、クラスでカプセル化したりするわけではないので、構造体にメソッドを定義する、その定義のしかたがちょっと理解しづらかったのでそのメモ。
理解しづらかったポイント
goを使って簡易的なHTTPサーバーを立てたときに、http.Handle
にServeHTTPのメソッドを持つ構造体を作るって箇所で詰まって色々調べた備忘録。
package main import ( "fmt" "net/http" ) // AppHandler アプリ type AppHandler struct { appName string } func (h *AppHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { fmt.Println(h.appName) } func main() { http.Handle("/", &AppHandler{appName: "myApp"}) if err := http.ListenAndServe(":8080", nil); err != nil { fmt.Println("エラー発生") } fmt.Println("サーバースタート") }
こういうコードを想定。特に最初???だったのはここ
func (h *AppHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
fmt.Println(h.appName)
}
これは
type AppHandler struct { appName string }
の構造体に ServeHTTP
メソッドを定義している。
https://golang.org/pkg/net/http/#Handler を確認すると、
type Handler interface { ServeHTTP(ResponseWriter, *Request) }
Handlerはinterface型なので、定義先はどんな型でもいいのだけれど、ServeHTTPというメソッドを持っていなくてはならない。
つまりAppHandlerの構造体であってもServeHTTPを定義することができる
func (構造体) (登録するfunc) {}
という形で定義が可能。
これでAppHandlerの構造体はServeHTTPというメソッドを持つことができ、http.Handleに登録して、指定したPortでHTTPリクエストを受け付けることができるようになる。
構造体以外にメソッドが定義できる。
HTTPサーバーを立てる際に以下のような定義の仕方もある。
func main() { http.ListenAndServe(":8080", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { res, err := w.Write([]byte(`Test`)) if err != nil { log.Println("Error") } log.Printf("%d", res) })) }
HTTPサーバーを立てる上で行っている動作は直観的でわかりやすいのであるが、HanderFuncメソッドは一体どこからきていた何なのかがわからず。これもドキュメントを確認すると下記のようになっている。
type HandlerFunc func(ResponseWriter, *Request)
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request)
HandlerFunc
が func(ResponseWriter, *Request)
の関数型として定義されていて、HanderFunc
型に ServeHTTP
が定義されている。これは構造体に ServeHTTP
を定義していたのと似ていると思う。特定のエイリアスを関数型として定義して、その関数型に関数を持たせるという個人的には摩訶不思議な言語仕様のなせる技なんだなーと。
goは言語仕様がシンプルで柔軟というはこういうところから来ているような気がしました。