Interface — Rubyのダックタイピングが型システムに出会った時
明示的なimplementsなし、メソッドが合えば自動的にインターフェースを満たす
Rubyのダックタイピング:「quackメソッドがあればアヒルだ。」ランタイムで確認。なければNoMethodError。
Goのinterface:「quackメソッドがあればQuacker型だ。」コンパイル時に確認。なければコンパイルエラー。
核心の違いはこれだ。同じ哲学だが、Rubyはランタイムで「うまくいけばいい」、Goはコンパイル時に「だめならデプロイできない」。
暗黙的実装 — implementsがない
Javaではclass Dog implements Animalと明示する。Goでは明示しない:
type Quacker interface { Quack() string }
type Duck struct{}
func (d Duck) Quack() string { return "ガー" }
// DuckはQuackerインターフェースを自動的に満たす
DuckがQuackerを実装するという宣言がどこにもない。Quack() stringメソッドがあるから自動的に適格。Rubyのダックタイピング哲学を型システムに持ち込んだもの。
空インターフェース = RubyのObject
interface{}(Go 1.18+ではany)は全ての型が満たすインターフェース。RubyのObjectクラスと同じ。あらゆる値を受けられるが、型安全性を放棄することになる。
io.Reader、io.Writer — 小さなインターフェースの力
Go標準ライブラリはとても小さなインターフェースを基本単位にする。io.ReaderはRead(p []byte) (n int, err error)メソッドひとつだけ要求。ファイル、HTTPレスポンス、文字列、圧縮ストリーム — 全てこの一つのインターフェースを満たす。
RubyのIOオブジェクトがreadメソッドを持つのと似ているが、Goではこれが型システムで強制される。
RubyからGoへ
Ruby: ダックタイピング(ランタイム確認)→ Go: interface(コンパイル時ダックタイピング)
Java: implements明示 → Go: 暗黙的実装(メソッドが合えば自動満足)
Ruby: Object(全ての親)→ Go: interface{}/any(全型受容)
インターフェースは小さく — io.Readerのようにメソッド1〜2個が理想的
メリット
- ✓ Rubyのダックタイピング哲学を維持しつつコンパイル時安全性まで保証
- ✓ implements宣言不要 — パッケージ間の依存性が緩やかになる
デメリット
- ✗ どのstructがどのinterfaceを満たすかコードだけでは分かりにくい
- ✗ interface{}(any)を乱用するとRubyの動的型付けと変わらなくなる