← 記事一覧へ

Go 学習記録(11):インターフェース

Go 基礎学習の第 11 回。文法、実例、学習の過程を記録します。

インターフェース

インターフェースは、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...)
}
この記事の日付について

旧サイトに記載された日付を保持しています。実際に書き始めた時期は、これより早い可能性があります。

同じシリーズ · Go の基礎学習

前の記事Go 学習記録(10):ポインタと nil ←次の記事Go 学習記録(12):パッケージとモジュール →