インターフェース
インターフェースの概念や用途が全然わからないので、インターフェースのチュートリアルを始める前に下調べしました。
なるべく消化できるようにわかりやすく、理解しやすく、咀嚼して説明を書いてみました。
参考
上から順に読むと理解が進みました。
初心者に送りたいinterfaceの使い方
Goの実装例で理解するダックタイピング
【Golang】Golangのinterfaceで知っておくとお得なTips
Goのinterfaceがわからない人へ
*補足
Goの実装例で理解するダックタイピング
サンプルコードが動かないので、以下に修正
type Income interface {
× calculate() string
◯ calculate() int / / 戻り値の型をstring型からint型へ
}
× (g google) calculate() int {
◯ func(g google) calculate() int { // "func"が抜けているので、追加
return g.baseSalary + (1000 * g.performance)
}
インターフェースとは何か
どんな型でも入れられる
package main
import "fmt"
func main() {
var i interface{}
i = 10
fmt.Println(i) // 10
i = "string"
fmt.Println(i) // string
// iは現在インターフェース型である
// そのため、例えば変数 i にある文字列と他の文字列を結合したい時
// インターフェース型とstring型は結合できないというエラーになる
fmt.Println("この中身は" + i) // invalid operation: (mismatched types interface {} and string)
// だから、インターフェース型からstring型に戻してあげる必要がある
// (型変更した物を受け取る変数) , ok := (型を変更したいインターフェース型の変数).(変更後の型名)
// okは、型変更できたかどうかをtrue、falseで返す
str_i , ok := i.(string)
if ok {
fmt.Println("この中身は" + str_i) // この中身はstring
}
}
ダックタイピング
"If it walks like a duck and quacks like a duck, it must be a duck"
(もしもそれがアヒルのように歩き、アヒルのように鳴くのなら、それはアヒルに違いない)
上記の文をインターフェースのダックタイピングに言い換えると、
"もし、構造体がインターフェース型と同じ関数を持っているなら、その構造体はインターフェース型に違いない"
ダックタイピングの流れ
1.インターフェースを宣言
2.そのインターフェースが持つ関数を宣言
3.構造体を宣言する
4.構造体にインターフェースで宣言した関数名と同じ関数を宣言する
5.結果、明示的にその構造体はインターフェースであると宣言しなくても、自動的にその構造体はインターフェース型になる
宇宙飛行士に例えると
1.宇宙飛行士というインターフェース型を宣言
2.宇宙飛行士ができることを宣言(例えば、宇宙にいく、ロケットの操縦をする)
3.とある青年を宣言する
4.その青年のできることを宣言(宇宙飛行士と同じで、宇宙にいく、ロケットの操縦をする)
5.わざわざその青年は宇宙飛行士であると宣言しなくても、宇宙に行ったり、ロケットを操縦するなら、彼は宇宙飛行士に違いない
インターフェースの利用法
例えば、アームストロングさんと毛利さんという宇宙飛行士を作りたいとします。
同じ宇宙飛行士でも、アームストロングさんは月へ、毛利さんは火星へ行こうとしているとします。
そこで先ほどの宇宙飛行士のインターフェース型がすることに"宇宙にいく"というメソッドがあリます。
しかし、同じ宇宙でも、アームストロングさんは月、毛利さんは火星、と目的地が違います。
そこで、"宇宙にいく"のメソッドに
アームストロングさんの場合は、"月にいく"
毛利さんの場合は、"火星にいく"
というように同じ"宇宙にいく"というメソッドでも、それぞれの違いを設定することができる。
宇宙飛行士の例を元にインターフェースを実装してみる
package main
import "fmt"
// 【ステップ1】 宇宙飛行士というインターフェースを宣言
type Astronaut interface {
//【ステップ2】宇宙飛行士のできることを宣言
go_to_space()
}
// 【ステップ3】とある青年を宣言
type Armstrong struct {
name string
destination string
experience int
}
// 【ステップ3】とある青年を宣言
type Mouri struct {
name string
destination string
experience int
}
// 【ステップ4】とある青年がすること(メソッド)を宣言(宇宙飛行士(インターフェース型)で設定したメソッド名(go_to_space)と同じ)
func (a Armstrong) go_to_space() {
fmt.Printf("%sへは%d回行ったけど、もう一回行きます!\n", a.destination, a.experience)
}
//【ステップ4】とある青年がすること(メソッド)を宣言(宇宙飛行士(インターフェース型)で設定したメソッド名(go_to_space)と同じ)
func (m Mouri) go_to_space() {
fmt.Printf("%sに行ってきます!\n", m.destination)
}
func main() {
//
armstrong := Armstrong {
name: "armstrong",
destination: "月",
experience: 1,
}
mouri := Mouri {
name: "mouri",
destination: "火星",
experience: 0,
}
// 【結果】 armstrongとmouriを明示的にAstronaut型だと宣言しなくても、Astronaut型のスライスに入れることができている
// つまり、自動的にarmstrong型とmouri型がAstronaut型になっている!!
astronauts := []Astronaut{armstrong, mouri}
greet(astronauts)
}
func greet(astronauts []Astronaut) {
for _, astronaut := range astronauts {
astronaut.go_to_space()
}
}