インターフェース
インターフェースは、Go の型システムとプログラム設計の重要な要素です。
1. ダックタイピング
ダックタイピング
歩き方、泳ぎ方、鳴き方がアヒルのようなら、アヒルとして扱う、という考え方です。
内部構造よりも、提供する振る舞いに注目します。
型のメソッド集合がインターフェースの全メソッドを含めば、その型はインターフェースを満たし、その値をインターフェース経由で使えます。
2. インターフェースを宣言する
type と interface を使い、次の形式で宣言します。
type 接口名 interface{
方法名1(参数列表) 返回值列表
方法名2(参数列表) 返回值列表
...
}
例:
type Duck interface{
Gaga()
Walk()
Swimming()
}
type pskDuck struct{
legs int
}
func (pd *pskDuck) Gaga(){
fmt.Println("嘎嘎嘎")
}
func (pd *pskDuck) Walk(){
fmt.Println("走")
}
func (pd *pskDuck) Swimming(){
fmt.Println("游泳")
}
func main(){
var duck Duck = &pskDuck{}
duck.Gaga()
duck.Walk()
duck.Swimming()
}
3. 複数のインターフェースを満たす
インターフェースは小さく、役割を絞ります。一つのメソッドだけを持つものもよく使われます。
type Duck interface{
Gaga()
}
type Duck2 interface{
Walk()
}
type Duck3 interface{
Swimming()
}
type pskDuck struct{}
、
func (pd *pskDuck) Gaga(){
fmt.Println("嘎嘎嘎")
}
func (pd *pskDuck) Walk(){
fmt.Println("走")
}
func (pd *pskDuck) Swimming(){
fmt.Println("游泳")
}
func main(){
var duck Duck = &pskDuck{}
var duck2 Duck2 = &pskDuck{}
}
例の pskDuck は Duck、Duck2、Duck3 を満たします。main の Duck 変数にも、Duck の全メソッドを持つので pskDuck の値を代入できます。
構造体のフィールドや関数の引数にも使えます。
振る舞いを柔軟に組み合わせられます。
コードの疎結合
一つの型が複数のインターフェースを満たせます。また、一つのインターフェースを複数の型が満たせます。
……
4. インターフェースと動的な値
型アサーション
値を加える計算機を例にします。
func add(a, b interface{}) interface{}{
switch a.(type){
case int:
ai, _ := a.(int)
bi, _ := b.(int)
return ai + bi
case int32:
ai, _ := a.(int32)
bi, _ := b.(int32)
return ai + bi
case float32:
ai, _ := a.(float32)
bi, _ := b.(float32)
return ai + bi
//……
default:
return 0
}
}
func main(){
fmt.Println(add(1, 2))
fmt.Println(add(1, 3))
fmt.Println(add(1.0, 2.0))
}
5. インターフェースの埋め込み
埋め込みにより、メソッドの要件を組み合わせて定義を再利用できます。
type MyWriter interface{
Write(string)
}
type MyReader interface{
Reader() string
}
type MyReaderWriter interface{
MyReader
MyWriter
ReadWriter()
}
type SreadWriter struct{}
func (srw *SreadWriter) Read(){
fmt.Println("read")
}
func (srw *SreadWriter) Write(){
fmt.Println("write")
}
func (srw *SreadWriter) ReadWriter(){
fmt.Println("readWriter")
}
func main(){
var mrw MyReaderWriter = &SreadWriter{}
mrw.Read()
}
6. スライスの変換で起きる間違い
スライス自体はインターフェース値として渡せます。ここで注意するのは、各 T がインターフェースを満たしていても、[]T をそのまま []interface{} に変換できないことです。その表現が必要なら、各要素を個別に変換します。
func mPrint(data ... interface{}){
for _, value := range data{
fmt.Println(value)
}
}
func main(){
//这样是支持的
var data = []interface{}{
"aaaaa",
19,
1800.00,
}
mPrint(data...)
}