Dyuichi Blog

Goのインタフェースとダックタイピング

Go言語のインターフェースは、他のオブジェクト指向言語(JavaやC++など)とは異なり、「明示的に実装を宣言しない」という特徴があります。この設計は、ダックタイピングの考え方に基づいており、Goならではの柔軟なプログラミングスタイルを実現しています。

本記事では、Goのインターフェースの仕組みやダックタイピングとの関係について解説し、コード例とともにその利点を紹介します。


1. Goのインターフェースの特徴

Goのインターフェースは、以下の特徴を持っています。

明示的な実装宣言が不要

「ある型が何をできるか(メソッドを持っているか)」のみを基準に適合を判断

疎結合で柔軟なコードが書ける

たとえば、以下のコードでは Dog 型が Speaker インターフェースを満たしていますが、implements のような明示的な宣言はありません。

gopackage main

import "fmt"

// Speaker インターフェース
type Speaker interface {
    Speak() string
}

// Dog 構造体
type Dog struct {
    Name string
}

// Speak メソッドを実装
func (d Dog) Speak() string {
    return d.Name + " says: Woof!"
}

// Speaker を受け取る関数
func MakeSound(s Speaker) {
    fmt.Println(s.Speak())
}

func main() {
    dog := Dog{Name: "Rex"}
    MakeSound(dog) // Rex says: Woof!
}

このコードでは、Dog 型が Speaker インターフェースを満たしているため、MakeSound(dog) のように Speaker 型の引数として渡すことができます。

この「メソッドがあれば自動的に適合する」という仕組みは、ダックタイピング に似ています。


2. ダックタイピングとは?

ダックタイピング(Duck Typing) とは、

「もし何かがアヒルのように歩き、アヒルのように鳴くなら、それはアヒルだと考えるべき」

という考え方です。

プログラミングにおいては、型が何であるかよりも、その型がどのようなメソッドを持っているかを重視する ことを意味します。

Goのインターフェースはまさにこの考え方を採用しており、型宣言なしに、メソッドの有無だけで適合を判断 します。

たとえば、Dog だけでなく CatSpeak メソッドを持っていれば、Speaker インターフェースを満たします。

gopackage main

import "fmt"

// Speaker インターフェース
type Speaker interface {
    Speak() string
}

// Dog 構造体
type Dog struct {
    Name string
}

func (d Dog) Speak() string {
    return d.Name + " says: Woof!"
}

// Cat 構造体
type Cat struct {
    Name string
}

func (c Cat) Speak() string {
    return c.Name + " says: Meow!"
}

func MakeSound(s Speaker) {
    fmt.Println(s.Speak())
}

func main() {
    dog := Dog{Name: "Rex"}
    cat := Cat{Name: "Mittens"}

    MakeSound(dog) // Rex says: Woof!
    MakeSound(cat) // Mittens says: Meow!
}

このように、DogCatSpeaker インターフェースに適合しているため、MakeSound 関数に渡すことができます。


3. 明示的にインターフェース適合をチェックする方法

「暗黙的に適合するのは便利だけど、可読性のために明示的に宣言したい」と思うこともあります。その場合、コンパイル時にインターフェース適合をチェックする方法 があります。

gopackage main

// Speaker インターフェース
type Speaker interface {
    Speak() string
}

// Dog 構造体
type Dog struct {
    Name string
}

// 明示的に Speaker を満たすことを宣言(コンパイル時チェック)
var _ Speaker = (*Dog)(nil)

// Speak メソッドを実装
func (d Dog) Speak() string {
    return d.Name + " says: Woof!"
}

この var _ Speaker = (*Dog)(nil) は、

  • Dog が Speaker を満たしていることを明示的にチェックする
  • もし Speak メソッドを実装していなければコンパイルエラーになる

という役割を持っています。


4. Goのインターフェースとダックタイピングのまとめ
言語インターフェースの実装方法
Java / C++implements InterfaceNamepublic class Dog : Speaker を明示的に宣言する必要がある
Goimplements などの明示的な宣言は不要。メソッドの有無だけでインターフェース適合が判断される
🎯 Goのインターフェースとダックタイピングのポイント

「型」よりも「できること」を重視する設計

メソッドがあるかどうかで適合が決まる(ダックタイピング)

明示的な宣言が不要なため、疎結合なコードを書きやすい

可読性のために _ Speaker = (*Dog)(nil) のようにチェックを追加できる

Go の「暗黙的に適合する柔軟なインターフェース設計」は、最初は違和感があるかもしれませんが、慣れるとシンプルで強力な設計思想 だと感じられるはずです! 🚀