導入
golangのオブジェクト指向言語ぽい機能を調べてみました。
エイリアス
type myInt int
こういうやつですね。これはもともとの型のエイリアスです。
元の型とは別の型ですが、以下のように相互に変換することができます。
var i int = 1
var j myInt = myInt(i)
var k int = int(j)
エイリアス化すれば、自由に機能(メソッド)を追加することができます。
JavaなどではBuilt-in(java.lang類)型は継承も機能追加もできないのと比較すると、Goらしい自由さを感じるところです。
var myInt int
func (i myInt) String() string {
return fmt.Sprintf("myInt --- %d", i)
}
この機能、enumでよく使われますね。
type MyType int
const (
A MyType = iota
B MyType
C MyType
)
ちなみに、superが欲しければ、
func (i MyType) String() string {
j := OrigType(i)
return "MyType" + j.String()
}
のような感じで取得できます。少し面倒ですが。
埋め込み型
type MyInt struct {
int
}
MyIntはintを要素に持ちながら、intの機能を持ちます。
エイリアスとの違いとしては、
- 多重継承できる
- superにアクセスするのが簡単
- 継承元以外の要素を含める
といったところかと思います。
多重継承によってメソッドが衝突した場合、衝突を解消しない限りコンパイルエラーになります。
type A struct {}
type B struct {}
func (*A) Hoge() string { return "A" }
func (*B) Hoge() string { return "B" }
// CはAとBを継承(埋め込み)
type C struct {
A
B
}
func (c *C) Hoge() string { return c.A.Hoge() + c.B.Hoge() }
例えばこんな風に衝突を解消することができるでしょう。
こんなコードが推奨されるか?というと疑問ですが。
どう使うか
個人的には以下の順序で考えます。
- structの要素か?(内部ロジックで使うのみなら)
- エイリアスか?(単純な型のコピーもしくは拡張であれば)
- 埋め込み型?(型を拡張しつつかつ、superや他の要素が必要な場合)
まずはhas_aでいいのではないかと考えます。
型の拡張の場合はまずエイリアスを検討します。
埋め込み型は上記を超える要件の場合のみとしたい所です。
aliasはわりとカジュアルに使えると思っていますが、埋め込み型はオブジェクト指向での継承の是非の議論と同じように使用に慎重であるべきかと思います。