2020-06-12
6/12
私はまだGolangのinterfaceが理解できていないようだ
shelpの実装中に次のようなコードを書いた。
※簡略化してある。実際のコミットは https://github.com/progrhyme/shelp/commit/bd3b037d80c3fe6e235dd0868123f7d7e264c3bc
////////////////////////////////////////////////////////////
// bodyとpartを組合せた型を作る
// partには基本型と拡張型がある
////////////////////////////////////////////////////////////
// body
type bodyI interface {
quantity() int
}
// bodyの実装
type coreS struct {
weight int
}
func (c *coreS) quantity() int {
return c.weight
}
// ====================
// part
type partI interface {
number() int
}
// partの実装
type legS struct {
num int
}
func (l *legS) number() int {
return l.num
}
// partの拡張型
type enhancedPartI interface {
partI
variation() int
}
// enhancedPartの実装
type bottomS struct {
legS
variety int
}
func (b *bottomS) variation() int {
return b.variety
}
////////////////////////////////////////////////////////////
// 複合型
// ①body + partな型
type artifactI interface {
bodyI
getPart() partI
}
// artifactの実装
type dollS struct {
coreS
part legS
}
func (d *dollS) getPart() partI {
return &d.part
}
// ====================
// ②body + enhancedPartな型
type robotI interface {
bodyI
getPart() enhancedPartI
}
// robotの実装
type toyRobotS struct {
coreS
part bottomS
}
func (tr *toyRobotS) getPart() enhancedPartI {
return &tr.part
}
書いてみるとややこしいが、上のような型定義を行った。
このとき、下のようなコードを書くとエラーになる。
func partNumber(a artifactI) int {
return a.getPart().number()
}
func main() {
bot := &toyRobotS{}
bot.coreS = coreS{weight: 10}
bot.part.num = 2
bot.part.variety = 3
fmt.Println(partNumber(bot))
}
interface enhancedPartI
は interface partI
を満たしているのではないかと思うのだが、 a.getPart()
で求められているのは partI
ですよ、とコンパイラが怒ってくる。
https://play.golang.org/p/7I4rQIA4KxQ
./prog.go:122:24: cannot use bot (type *toyRobotS) as type artifactI in argument to partNumber:
*toyRobotS does not implement artifactI (wrong type for getPart method)
have getPart() enhancedPartI
want getPart() partI
仕方ないので、次のように取得したいpart型によって toyRobotS
のメソッドを分けることにした。
// robotIのインタフェースを変更
type robotI interface {
bodyI // ここはartifactIでもよさそう
getEnhancedPart() enhancedPartI
}
// NOTE: toyRobotSの型定義は同じ
// artifactIを実装
func (tr *toyRobotS) getPart() partI {
return &tr.part
}
// robotIを実装
func (tr *toyRobotS) getEnhancedPart() enhancedPartI {
return &tr.part
}
そしたら怒られなくなった。
https://play.golang.org/p/bI1N3dUb5h0
想像するに、partNumber関数の中ではartifactI型の値を受け取って、a.getPart()を呼んだらpartI型の値が返ってくることを期待しているのに、ehancedPartI型の値が返ってくるのでそんなの知らんよ、ということだろうか。
最終更新 2020-07-18: [memo] Archive 2020061{2,3} (bec4833cc)