【ITニュース解説】Decoding Golang Interfaces: Why Your Struct Fits an Interface It Never Met
2025年09月09日に「Dev.to」が公開したITニュース「Decoding Golang Interfaces: Why Your Struct Fits an Interface It Never Met」について初心者にもわかりやすいように丁寧に解説しています。
ITニュース概要
Go言語では、インターフェースの実装を明示的に宣言する必要がない。構造体などがインターフェースの要求するメソッドをすべて持っていれば、自動的にそのインターフェースを実装したと見なされる。この「暗黙的な実装」がGoの大きな特徴である。
ITニュース解説
プログラミング言語Golangのインターフェースは、JavaやC#といった他の言語の経験者にとって、初めは少し不思議に感じられるかもしれない。具体的には、ある構造体が特定のインターフェースを「実装する」と明示的に宣言していないにもかかわらず、そのインターフェースを要求する関数の引数として渡せてしまうコードが問題なく動作する場面がある。この挙動の背景には、Golangの設計思想を象徴する「暗黙的なインターフェースの実装」という強力な機能が存在する。この仕組みを理解するためには、まず「具象型」と「抽象型」という二つの型の概念から始める必要がある。
プログラムにおける「具象型」とは、データを保持するための具体的な設計図であり、Golangでは構造体(struct)がその代表例だ。例えばtype A struct { abc string }という定義は、abcという名前の文字列データを一つ格納できるメモリ領域の設計図を意味する。この設計図からa := &A{}のようにインスタンスを生成すると、プログラムのメモリ上に実際にデータを保持できる実体が作られる。これは具体的な存在であるため、具象型と呼ばれる。
一方、「抽象型」は具体的なデータを保持せず、代わりに「どのような振る舞い(メソッド)を持つべきか」という一連のルール、すなわち契約を定義する。Golangのインターフェースがこれにあたる。type I interface { f1() }という定義は、データ構造を一切指定せず、「引数を取らず、戻り値もないf1()という名前のメソッドを持っていること」だけを要求する契約である。このインターフェースは、型が何であるか、どのようなデータを持っているかには関知せず、ただ定義された振る舞いを提供できるかどうかだけを問う。
この二つの型の違いを理解した上で、Golangの核心的なルールに目を向ける。それは、「ある型が、インターフェースが要求するすべてのメソッドを実装していれば、その型は自動的に、そして暗黙的にそのインターフェースを満たすとみなされる」というものである。Javaのようにclass MyClass implements MyInterfaceと明示的に宣言する必要はない。Golangのコンパイラは「実装するつもりだ」という宣言ではなく、「実際に何ができるか」という事実だけを見て判断する。
提示されたコード例にこのルールを適用してみよう。まず、インターフェースIはf1()というメソッドを持つことを契約として要求している。次に、構造体Aへのポインタ型である*Aは、func (a *A) f1()というメソッドを持っている。このメソッドは、名称(f1)とシグネチャ(引数なし、戻り値なし)がインターフェースIの要求と完全に一致する。この事実をもって、Golangコンパイラは「型*AはインターフェースIの契約を満たしている」と自動的に結論付ける。そのため、*A型の変数aを、I型の引数を期待する関数wantsIに渡すことが可能になる。関数wantsIの内部では、引数iはインターフェース型の値として扱われるが、その内部には元の具体的な型(この場合は*A)の情報と、実際のデータへのポインタが保持されている。そしてi.f1()が呼び出されると、Golangは内部に保持された型情報に基づき、*Aに紐づく具体的なf1()メソッドを実行する。
ここでさらに重要な詳細がある。それはメソッドが定義される際のレシーバの型、つまりポインタレシーバか値レシーバかという点だ。func (a *A) f1()のようにレシーバがポインタ型(*A)で定義されたメソッドは、ポインタ型*Aのメソッドセットに属する。しかし、値型であるAのメソッドセットには含まれない。この「メソッドセット」の概念により、*AはインターフェースIを実装するが、値型のAは実装しない、という明確な区別が生まれる。もしポインタではなく値(a_val := A{})を生成してwantsI関数に渡そうとすると、「型AはインターフェースIを実装していません。なぜならf1メソッドはポインタレシーバを持つからです」という内容のコンパイルエラーが発生する。このエラーメッセージは、メソッドセットのルールが厳密に適用されていることを示している。
この暗黙的なインターフェース実装という設計は、Golangの思想の根幹をなすものである。インターフェースは、型が何であるかという継承や分類(is-a関係)ではなく、何ができるかという振る舞い(can-do関係)に焦点を当てている。これにより、関数とそれが利用する具体的なデータ構造を分離、つまり疎結合にすることができる。wantsIのような関数は、Aという構造体の存在を一切知ることなく、f1()メソッドを持つあらゆる型を受け入れることができる。この柔軟性こそが、テストが容易で保守性の高いソフトウェアの構築を可能にする鍵なのである。