「インタフェースがnilであれば、それに関するどのメソッドを起動してもパニックになる」の意味
O'Reilly の初めてのGo言語の文章がしっくりこない
インタフェースがnilであるかどうかは、そのインタフェースに関するメソッドを起動できるかどうかを示すことになります。前に説明したように、具象型のインスタンスがnilであっても、それに関するメソッドを呼び出すことができますから、nilの具象インスタンスを代入されたインタフェース変数に関するメソッドを起動できるのは問題がありません。インタフェースがnilであれば、それに関するどのメソッドを起動してもパニックになります。インタフェースが非nilならば、それに関するメソッドを起動できます(ただし、値がnilの場合、割り当てられた型のメソッドがnilをきちんと処理できなければ、やはりパニックになります)。
と書いてあり、「インタフェースがnilであれば、それに関するどのメソッドを起動してもパニックになります。」の部分がしっくりこなかったので動かして理解してみた
一旦手を動かしてみる
interfaceと基底型をnilで定義して、メソッドを実行してみる
package main
import (
"fmt"
)
// サンプルのインターフェース
type MyInterface interface {
Method()
}
// 実装構造体
type MyStruct struct{}
func (m *MyStruct) Method() {
fmt.Println("Method called!")
}
func main() {
var ms *MyStruct
fmt.Println(ms == nil) // true
var i MyInterface // interface{}の代わりにanyでも同じ
fmt.Println(i == nil) // true
i = ms
i.Method()
}
//--------------------------------
true
true
false
Method called!
panicせず普通にMethod()
が実行できてしまう・・・
そもそも文中しっくりこなかった「インタフェースがnil」とは
インタフェースは「ベースとなる型(基底型)へのポインタ」と「ベースとなる値へのポインタ」の組で実装されています。型が非nilならば、インタフェースはnilにはなりません
インタフェースがnilであるとみなされるためには、型と値の両方がnilでなければなりません
と書いてありました。
また、この文章の近くにあったサンプルコードが下記で
var s *string
fmt.Println(s == nil) // true
var i interface{} // interface{}の代わりにanyでも同じ
fmt.Println(i == nil) // true
i = s
fmt.Println(i == nil) // false
ベースとなる型(基底型)(*string)も、ベースとなる値(s)へのポインタもnil
なのに、s
をi
に代入するとnil
でなくなっています。
これは、i = s
のように具体的な値を代入し、
型情報: *string
(ベースとなる型)
値情報: s
の値
が入り、s
はnil
なので値の情報は入っていないとして、
型の情報が入っており、インタフェースがnilであるとみなされるためには、型と値の両方がnilでなければならない ので、i
がnil
ではなくなっているということ
つまり、どうしたらpanicになるかというと
冒頭でしっくりこなかった「インタフェースがnilであれば、それに関するどのメソッドを起動してもパニックになります。」という文章をコードにすると多分下記で
package main
import (
"fmt"
)
// サンプルのインターフェース
type MyInterface interface {
Method()
}
func main() {
var i MyInterface // interface{}の代わりにanyでも同じ
fmt.Println(i == nil) // true
i.Method()
}
ただinterfaceのMethod()を実行するとpanicになる。
そりゃそうって話ではありますが、
- インタフェースは「ベースとなる型(基底型)へのポインタ」と「ベースとなる値へのポインタ」の組で実装されている
- インタフェースがnilであるとみなされるためには、型と値の両方がnilでなければならない
ので、この場合のinterfaceはnilになり、panicを起こす、ということが学びになりました。
Discussion