【ITニュース解説】Go (Golang) Basic - Interfaces
2025年09月28日に「Dev.to」が公開したITニュース「Go (Golang) Basic - Interfaces」について初心者にもわかりやすく解説しています。
ITニュース概要
Goのインターフェースは、複数の型が共通の振る舞いを定める。型がメソッド実装で自動的に適合すれば、型に縛られない柔軟な関数が作れ、コードの再利用性が向上する。
ITニュース解説
Go言語でプログラミングをする際、これまで構造体を使って独自のデータ型を定義し、それにメソッドを紐づけることで、データとそのデータが持つ振る舞いを一つのまとまりとして扱えることを学んだ。これは、整理された自己完結型のコンポーネントを作成する上で非常に強力な方法である。しかし、もし様々な種類のデータ型、例えばUser構造体、Product構造体、Invoice構造体といった、それぞれ異なる具体的な型を持つにもかかわらず、共通の振る舞い、例えば「ファイルを保存する」という処理を実行できるような関数を書きたい場合、どのようにすればよいだろうか。構造体だけでは、それぞれの具体的な型に対応した関数を個別に作成する必要があり、コードが冗長になってしまう可能性がある。このような状況でその真価を発揮するのが、Go言語の「インターフェース」である。インターフェースは、Go言語が提供する非常に強力な機能の一つであり、柔軟で抽象的なコードを書くために欠かせない概念だ。
インターフェースとは一体何だろうか。Go言語におけるインターフェースは、メソッドの「シグネチャ」のセットを定義する型である。シグネチャとは、メソッドの名前、引数の型、そして戻り値の型のことだ。インターフェース自体はデータを持たず、具体的な値も持たない。それはあくまで「契約」であると理解すると分かりやすいだろう。この契約は「私のグループの一員として扱われたいならば、これらの特定のメソッドを必ず持っていなければならない」と宣言しているようなものだ。例えば、SoundMakerというインターフェースを定義する場合、それはMakeSound()という名前で、引数なし、戻り値が文字列のメソッドを持つことを要求する。このインターフェースは、そのメソッドが実際にどのように音を出すか、具体的な実装には一切関与しない。ただ、そのメソッドが存在することだけを要求するのだ。
具体的なインターフェースの定義は以下のようになる。
1package main 2 3import "fmt" 4 5// これは契約である。 6// SoundMakerとして扱われたい型は、文字列を返す 7// MakeSound()というメソッドを必ず持たなければならない。 8type SoundMaker interface { 9 MakeSound() string 10}
このコードでは、SoundMakerという新しいインターフェース型を定義している。このインターフェースは、MakeSound()という名前のメソッドを一つだけ持っている必要があることを示している。このメソッドは引数を受け取らず、string型の値を返すものと定められている。
Go言語のインターフェースの大きな特徴は、その実装方法にある。JavaやC#のような他のプログラミング言語では、ある型が特定のインターフェースを実装することを明示的に宣言するためにimplementsのようなキーワードを使用することが一般的だ。しかし、Go言語ではそのようなキーワードは不要である。Go言語では、ある型がインターフェースで定義されている全てのメソッドを「暗黙的に」実装している場合、その型はそのインターフェースを満たしているとみなされる。つまり、特別な宣言なしに、ただ必要なメソッドを定義するだけでインターフェースを実装したことになるのだ。
先ほどのSoundMakerインターフェースを満たす二つの異なる型を作成してみよう。
1// (上記のコードに続く) 2 3// --- 最初の型: Dog --- 4type Dog struct { 5 Name string 6} 7 8// DogはMakeSound()メソッドを持つため、SoundMakerインターフェースを満たす。 9func (d Dog) MakeSound() string { 10 return "Woof!" 11} 12 13// --- 二番目の型: Cat --- 14type Cat struct { 15 Name string 16} 17 18// CatもMakeSound()メソッドを持つため、SoundMakerインターフェースを満たす。 19func (c Cat) MakeSound() string { 20 return "Meow!" 21}
この例では、DogとCatという二つの異なる構造体を定義している。それぞれの構造体はNameというフィールドを持つが、それとは別に、両方ともMakeSound()というメソッドを持っている。DogのMakeSound()メソッドは"Woof!"を返し、CatのMakeSound()メソッドは"Meow!"を返す。この時点で、Go言語のコンパイラは、Dog型とCat型の両方がSoundMakerインターフェースの契約(MakeSound() stringメソッドを持つこと)を満たしていると自動的に判断する。たとえDogとCatが全く異なる構造体であっても、インターフェースが要求する共通の振る舞いを持っているため、両方ともSoundMakerとして扱われる資格があるのだ。
インターフェースが最も威力を発揮するのは、この「共通の振る舞い」を利用して関数を作成する時である。インターフェースを関数の引数として使用することで、その関数は特定の具体的な型ではなく、インターフェースが定義する振る舞いにのみ依存するようになる。これにより、極めて柔軟な関数を作成できるようになる。
SoundMakerインターフェースを受け取る関数letThemSpeakを定義してみよう。
1// (上記のコードに続く) 2 3// この関数は、SoundMakerインターフェースを満たすあらゆる型を受け入れることができる。 4func letThemSpeak(s SoundMaker) { 5 fmt.Println("The sound is:", s.MakeSound()) 6} 7 8func main() { 9 // DogとCatのインスタンスを作成 10 myDog := Dog{Name: "Buddy"} 11 myCat := Cat{Name: "Whiskers"} 12 13 // 同じ関数にmyDogとmyCatの両方を渡すことができる 14 letThemSpeak(myDog) // 出力: The sound is: Woof! 15 letThemSpeak(myCat) // 出力: The sound is: Meow! 16}
letThemSpeak関数はSoundMakerインターフェース型の引数sを受け取る。この関数は、sがDogなのかCatなのか、あるいはSoundMakerインターフェースを満たす別のどのような型なのかを知る必要がない。ただ、sがMakeSound()メソッドを持っていることだけを知っていれば良いのだ。そのため、myDog(Dog型)とmyCat(Cat型)という全く異なる具体的な型のインスタンスを、同じletThemSpeak関数に渡して実行することが可能になる。これにより、コードの再利用性が大幅に向上し、特定のデータ型に強く結合されない、汎用的な関数を作成できる。これは、コードをよりモジュール化し、保守しやすくする上で非常に強力なアプローチである。
インターフェースは、Go言語において、きれいで抽象的、そしてテストしやすいコードを書くための中心的な概念である。具体的な型(構造体など)に直接依存するのではなく、契約(インターフェース)を定義し、それに依存することで、構築するシステムははるかに柔軟で拡張性の高いものになる。
今回の解説を通じて、以下の主要な点を理解できたはずだ。 第一に、インターフェースがメソッドシグネチャのセットとして定義されること。 第二に、構造体に必要なメソッドを実装することで、インターフェースが暗黙的に満たされること。 第三に、特定の型ではなくインターフェースに対して動作する汎用的な関数を作成できること。
これらの知識は、より堅牢で効率的なGoプログラムを書くための重要な基盤となるだろう。