いろいろ備忘録日記

主に .NET とか Go とか Flutter とか Python絡みのメモを公開しています。

Goメモ-27 (空インターフェース, Empty interface, Tour of Go)

概要

Tour of Go の - Empty interfaces についてのサンプル。

tour.golang.org

今回は、空インタフェースについて。

Goには、interface{}という型があります。空インターフェースといいます。

この型、他の言語でいう Object 型みたいなイメージです。

つまり、どんな型も interface{} になれる。

当然といえば、当然で interface{} ということは定義されているメソッドが一つもないインターフェースってことです。

Goでは、対象のインターフェースを明示的に implements すると宣言しなくても、インターフェースに定義されているメソッドと同じシグネチャで定義していれば、そのインターフェースを実装していることになります。

なので、一つも定義を持たないインターフェースは、みんな何もしなくても実装出来ていることになります。

なので、interface{}は、どの型からも変換可能となります。

サンプル

package tutorial

import "fmt"

type (
    myIf interface {
    }

    myIfImpl struct {
    }

    data struct {
        value string
    }
)

func (d *data) String() string {
    return fmt.Sprintf("[data] value:%v", d.value)
}

func (m *myIfImpl) String() string {
    return "myIfImpl"
}

// EmptyInterface は、 Tour of Go - The empty interface (https://tour.golang.org/methods/14) の サンプルです。
func EmptyInterface() error {
    // ------------------------------------------------------------
    // 空のインターフェース
    // Go言語において、メソッドを一つも持たないインターフェースを interface{} で表す.
    // このインターフェースを空のインターフェースと呼ぶ。
    //
    // Go言語では、この空のインターフェースは他の言語の Object 型に相当する.
    // つまり、 interface{} は任意の型の値を保持出来る.
    // ------------------------------------------------------------
    var (
        v interface{}
    )

    v = 100
    p(v)

    v = "helloworld"
    p(v)

    v = true
    p(v)

    v = &data{value: "data-value"}
    p(v)

    v = &myIfImpl{}
    p(v)

    var inf myIf = v
    p(inf)

    return nil
}

func p(v interface{}) {
    fmt.Printf("%v\n", v)
}

try-golang/tutorial_gotour_20_empty_interface.go at master · devlights/try-golang · GitHub

実行すると以下な感じ。

[Name] "tutorial_gotour_empty_interface"
100
helloworld
true
[data] value:data-value
myIfImpl
myIfImpl

インターフェースの合成について

C#やJavaなどの言語では、インターフェースの継承という概念があります。そのままの意味ですが、とあるインターフェースはとあるインターフェースを継承しているという意味となります。

各インターフェースをfatにせずに、シンプルに定義して、特定の単位でそれをまとめるインターフェースとかはよく作ったりします。

Goには、継承という概念はありません。クラスという概念がないので当然ですが。では、複合インターフェースみたいなのは作れないのか?ってなりますが、そんなことはなく、合成インターフェースという形で作ります。

合成(Composition) という概念は、オブジェクト指向ではよく聞く言葉ですね。よく継承と対比されます。 ( Inheritance vs Composition)

インターフェースの合成は以下のようにします。

type (
    // 普通のインターフェース1
    if01 interface {
        f1() string
    }

    // 普通のインターフェース2
    if02 interface {
        f2() string
    }

    // 合成インターフェース. if01とif02というインターフェースを合成している.
    // このインターフェースをするには if01とif02のインターフェース定義を満たす必要がある
    ifComposition interface {
        if01
        if02
    }
)

そのまんまです。合成したいインターフェースを並べます。

以下、ちょっとしたサンプルです。

package interface_

import "fmt"

type (
    // 普通のインターフェース1
    if01 interface {
        f1() string
    }

    // 普通のインターフェース2
    if02 interface {
        f2() string
    }

    // 合成インターフェース. if01とif02というインターフェースを合成している.
    // このインターフェースをするには if01とif02のインターフェース定義を満たす必要がある
    ifComposition interface {
        if01
        if02
    }

    // 実装
    compositionImpl struct{}
)

// impl: if01.f1
func (c *compositionImpl) f1() string {
    return "hello"
}

// impl: if02.f2
func (c *compositionImpl) f2() string {
    return "world"
}

// Composition は、 Goのインターフェースのコンポジション (合成) についてのサンプルです.
func Composition() error {
    // ----------------------------------------------------------------
    // インターフェースのコンポジションについて
    //
    // Goでは、インターフェースを合成する場合、合成インターフェースを定義して
    // 属性に各インターフェースを列挙していく.
    //
    // io.ReadCloser, io.ReadWriter などが合成インターフェースの代表例
    // ----------------------------------------------------------------
    var (
        c  = &compositionImpl{}
        f1 = func(i if01) {
            fmt.Println(i.f1())
        }
        f2 = func(i if02) {
            fmt.Println(i.f2())
        }
        f3 = func(i ifComposition) {
            fmt.Println(i.f1(), i.f2())
        }
    )

    // 具象型からインターフェースへ
    var v ifComposition = c

    // 合成インターフェースは、合成元となっている各インターフェースを名乗ることが出来る
    f1(v)
    f2(v)
    f3(v)

    return nil
}

実行すると以下のようになります。

[Name] "interface_composition"
hello
world
hello world

過去の記事については、以下のページからご参照下さい。

  • いろいろ備忘録日記まとめ

devlights.github.io

サンプルコードは、以下の場所で公開しています。

  • いろいろ備忘録日記サンプルソース置き場

github.com

github.com

github.com