タイトルそのままです。
package main
import (
"fmt"
"reflect"
)
type Fooer interface {
Foo()
}
type Fooo struct{}
func (f *Fooo) Foo() {
if f == nil {
fmt.Println("nil!!")
} else {
fmt.Println("not nil!!")
}
}
func newFooer() Fooer {
return &Fooo{}
}
func bar(f Fooer) {
f.Foo()
}
func main() {
// 型を代入したあとnilを入れても型情報は消えない
z := &Fooo{}
fmt.Println(reflect.TypeOf(z)) // *main.Fooo
z = nil
fmt.Println(reflect.TypeOf(z)) // *main.Fooo
z.Foo() // nil!!
bar(z) // nil!!
// 型情報を持ったnilを入れて、そのあとただのnilを入れても型情報は消えない
y := (*Fooo)(nil)
fmt.Println(reflect.TypeOf(y)) // *main.Fooo
y = nil
fmt.Println(reflect.TypeOf(y)) // *main.Fooo
y.Foo() // nil!!
bar(y) // nil!!
// interfaceを返す関数から変数を代入したあとにnilを入れると型情報が消える
x := newFooer()
fmt.Println(reflect.TypeOf(x)) // *main.Fooo
x = nil
fmt.Println(reflect.TypeOf(x)) // <nil>
// panicになる x.Foo()
// panicになる bar(x)
}
発端はクリーンアーキテクチャをやろうと思いサンプルを探すと、この例のように生成系でinterfaceを返しているものが多いです。
依存関係の逆転をしたいので、接合部分をinterfaceにするというのは素直だと思いますが、Goでは微妙なところもありそうです。
Go言語のInterfaceの考え方、Accept interfaces,return structsに従ったほうが、大体は良さそう。
クリーンアーキテクチャをするならAccept interfaces,return structs
に従って生成してからDIで渡すところでinterfaceを使うようにしたほうが良さそうです。