Golangのインターフェースは拡張していいものなのか?

早速Golangで頭を抱えている

Goには「このインターフェースを実装するぞ!」という明示的な宣言がない。errorインターフェースがいい例だが、Error() stringというシグニチャのメソッドがある構造体は何でもerrorとして扱うことできるようになる。ダックタイピングのような挙動を示す

そんなインターフェースの型によって分岐する単純なコードを考える

type Animal interface {
    Bark() string
}

// animalを実装したCat
type Cat struct {}

func (cat *Cat) Bark() string {
    return "Nyan"
}

// animalを実装してないApple
type Apple struct{}

func whatIs(x any) {
    switch x.(type) {
    case Animal:
        fmt.Println("Animal")
    default:
        fmt.Println("Not Animal")
    }
}

これを実行すると

func main() {
    whatIs(&Cat{}      // => Animalと出力
    whatIs(&Apple{})    // => Not Animalと出力
}

ここで迂闊にAnimalインターフェースを修正してしまった

type Animal interface {
    Bark() string
    Run()
}

そしてCatの修正を忘れてを実行すると

func main() {
    whatIs(&Cat{}      // => Not Animalと出力
    whatIs(&Apple{})    // => Not Animalと出力
}

Catを直していないのでNot Animalになってしまった・・・

明示的なインターフェース実装宣言がないので警告も出ずコンパイルも通ってしまうが意図せぬバグとなる可能性がある

安全なコードを書くという観点でどう扱えばいいのか悩ましい

そもそもインターフェース型でディスパッチすべきではない???

型で分岐しなければ問題ない。まあ、そうなんだけど納得できる答えではないなあ

モノをインターフェースにしない???

これはJavaのインターフェースの考え方だが、モノではなく能力をインターフェースにする。今回の場合はAnimalではなくBarkableのような名前のインターフェースを用意し、

type Barkable interface {
    Bark() string
}

ここにRunというメソッドを足そうというやつはいないはずだ・・・

テストかけ

ど正論、でもコンパイル型言語はそういうとこのミスはコンパイラに見つけて欲しいよな

結論

わからん