rubyしか使ったことがないプログラマーがgolangを勉強した時にinterfaceの理解でつまづいたので、勉強したことをまとめてみます。
個人的なメモの要素の方が強いので、みにくいかもしれませんが、それでも役に立つ人もいると思って書きました。
interfaceとは?
- メソッドの型だけを定義したもの
- golangのinterfaceは型の一つ
- stringやint64が関数を持っているように、interfaceで型を宣言してその型に関数を持たせることができる
type <型名> interface {
メソッド名(引数の型, ...) 返り値の型, ...
..
..
}
こんな感じでinterfaceというものを定義することができます。
interfaceを使うとどんなメリットがある?
teratailのこの質問を参考にしました。
package main
import (
"fmt"
)
// Fooに関する定義
type Foo struct{}
func (f *Foo) Name() string {
return "Foo"
}
// Barに関する定義
type Bar struct{}
func (b *Bar) Name() string {
return "Bar"
}
// Namedというinterfaceを定義
// Named interfaceによると、このinterfaceによって
// Name() というメソッドを持つことが強制される。
type Named interface{
Name() string
}
// named.Name()の結果を出力する関数。
// (named Named)となっているので、変数namedがNamed interfaceを
// 実装していることを保証している。
// さらにNamed interfaceによって変数namedがName()メソッドを持つことが
// 保証される。
func CallName(named Named) {
fmt.Println(named.Name())
}
func main() {
foo := new(Foo)
bar := new(Bar)
CallName(foo)
CallName(bar)
}
この様に、同一の特徴を持った値(この場合 foo もしくは bar)を共通的に取扱いたい場合に interface を用います。これを Named を使わずやろうと思うとそれぞれの型を引数に持った CallNameFoo もしくは CallNameBar を実装する必要があります。
A tour of Goの例
package main
import (
"fmt"
"math"
)
// Abserというinterfaceを定義
type Abser interface {
Abs() float64
}
func main() {
// interface typeを持った値は、そのinterfaceにあるメソッドを持っている値ならなんでも持つことができる。
// ここの例でいうと、Abs()メソッドさえ持っていれば、Abser型として認識されるということ。
var a Abser
f := MyFloat(-math.Sqrt2)
v := Vertex{3, 4}
// type MyFloatはAbs()を持っているのでa = fとしてもエラーにならない
a = f // a MyFloat implements Abser
// struct VertexはAbs()を持っているのでa = &vとしてもエラーにならない
a = &v // a *Vertex implements Abser
// In the following line, v is a Vertex (not *Vertex)
// and does NOT implement Abser.
a = v
fmt.Println(a.Abs())
}
type MyFloat float64
func (f MyFloat) Abs() float64 {
if f < 0 {
return float64(-f)
}
return float64(f)
}
type Vertex struct {
X, Y float64
}
func (v *Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
interfaceは暗黙的に実装される
package main
import "fmt"
type I interface {
M()
}
type T struct {
S string
}
// This method means type T implements the interface I,
// but we don't need to explicitly declare that it does so.
func (t T) M() {
fmt.Println(t.S)
}
func main() {
var i I = T{"hello"}
i.M()
}
A tour of GoのInterfaces are implemented implicitly
をみていた時に、最初は意味がわからなかったのですが、上に書いてあることを理解した後だと、当たり前じゃんくらいの感覚になります。
この例では
type I interface {
M()
}
となっているので、M()メソッドを持っていさえすればI interfaceを実装していると認識されます。
fmt.Println(t.S)
}
というメソッドが定義されています。
このメソッドの定義によってtype TがM()を持っていることになるので、同時にinterface Iを実装していることになりますが、それを明示的に宣言する必要はないということを言っています。
interfaceの値は内部的には値と型のタプルとして認識されている
A tour of GoのInterface valuesのところです。
golangのinterfaceは内部的には値と型のタプルとして認識されています。
type I interface {
M()
}
type T struct {
S string
}
func (t *T) M() {
fmt.Println(t.S)
}
func main() {
// iという変数をinterface Iで定義
var i I
// こうするとここでのiは
// (&{"Hello"}, 型T)という形で内部的に認識されている。
i = &T{"Hello"}
}
empty interface
A tour of GoのThe empty interfaceがわからなかったので調べました。
基本的に
Go言語 - 空インターフェースと型アサーション - 覚えたら書く
をみたらわかると思います。
empty interfaceはどんな型の値でも受け取れる関数を作るために利用されます。
例えばfmt.Printのようなもの。
func funcName(any interface{}) {
...
}
というふうに記述することでどんな型の値でも受け取れる関数が作れます。
参考にさせていただいたサイト
teratail
【Golang】Golangのinterfaceで知っておくとお得なTips
A tour of Go
Go言語 - 空インターフェースと型アサーション - 覚えたら書く