tanish-kr's learning log

Learning output log

Go

インタフェース

インタフェース型は、他の型の振る舞いに関する一般化あるいは抽象化を表す。Goのインタフェースは、具象型が満足するすべてのインタフェースを宣言する必要はない。

インタフェースは抽象型である。

インタフェース型

io.Writerの例

package io
// io.Writerはバイトが書き込める全ての方の抽象化を提供している
type Writer interface {
    Write(p []byte) (n int, err error)
}

インタフェースはインタフェース内に別のインタフェースを埋め込むことができる

package io

type Reader interface {
    Read(p []byte) (n int, err error)
}

type Closer interface {
    Close() error
}

type ReadWriter interface {
    Reader
    Writer
}

type ReadWriteCloser interface {
    Reader
    Writer
    Closer
}

インタフェースを満足する

インタフェースが要求している全てのメソッドを型が保持していれば、その型はそのインタフェースを満足するといえる。

interface{}型は、それを満足する具象型に関しては何も伝えていない。空インタフェース型と呼ばれる。

インタフェースの満足は、関係している2つの型のメソッドにだkで依存しているので、具象型とその具象型が満足するインタフェースの間に関係性を宣言する必要はない。

flag.Duration関数はtime.Duration型のフラグ変数を生成し、Stringメソッドで表示されたものと同じ表記を含む、さまざまな使いやすい形式でユーザが期間を指定することを可能にする。

flagインタフェース

flag.Duration

package flag

var period = flag.Duration("period", 1*time.Second, "sleep period")

func main() {
    flag.Parse()
    fmt.Printf("Sleeping for %v...", *period)
    time.Sleep(*period)
    fmt.Println()
}

flag.Value

package flag

type Value interface {
    String() string // コマンドラインのヘルプメッセージで使うフラグの値
    Set(string) error // stringをパースして、フラグの値を更新する
}

インタフェース値

インタフェース型の値、インタフェース値は概念的に具象型とその型の値という二つの構成要素を持っている。それらはインタフェースの動的な型と動的な値と呼ばれている。概念モデル上では、型記述子が型の名前やメソッドなどの個々の型に関する情報を提供する。インタフェース値では、型要素は適切な型記述子で表現されている。

  • インタフェースのゼロ値は、その型要素と値要素の両方がnilに設定されている
  • インタフェース値の動的な型が何であるかはコンパイル時にはわからないので、インタフェースを通した呼び出しは動的なディスパッチを使わなければならない。型記述子からメソッドのアドレスを取得し、そのアドレスへの間接的な呼び出しを行うコードを生成しなければならない。

インタフェースの比較

  • ==!=で比較可能
  • 二つのインタフェース値が比較されて同じ動的な方を持っていても、その型が比較可能でなければ(例えばスライス)、比較は失敗しパニックになる

nilポインタを含むインタフェースはnilではない

値を全く含んでいないnilインタフェース値は、たまたまnilであるポインタを含んでいるインタフェース値と同じではない

const debug = true

func main() {
    var buf *bytes.Buffer
    if debug {
        buf = new(bytes.Buffer) // 出力の収集を有効にする
    }
    // ここでio.Writerに変更すると解決
    // var buf io.Writer
    f(buf) // WARNING: 微妙に正しくない
    if debug {
        // ... bufを使用
    }
}

// outがnilでなければ、出力はoutへ書き出される
func f(out io.Writer) {
    if out != nil {
        out.Write([]byte("done!\n")) // パニック: nilポインタ参照
    }
}

sort.Interfaceでのソート

sort.Interface

列内でソートするアルゴリズムは、列の長さ、二つの要素を比較するための手段、二つの要素を交換するための方法が必要。これらの三つのメソッドを自走した方を定義する必要がある。

package sort

type Interface interface {
    Len() int
    Less(i, j int) bool // i, jは列要素のインデックス
    Swap(i, j int)
}
type StringSlice []string

func (p StringSlice) Len() int { return len(p) }
func (p StringSlice) Less(i, j int) bool { return p[i] < p[j] }
func (p StringSlice) Swap(i, j int)  { p[i], p[j] = p[j], p[i] }

sort.Sort(StringSlice(names))
sort.Sort(sort.Reverse(StringSlice(names))) // 降順
  • 降順にはsort.Reverseを使用する。reverseに対するLessメソッドは埋め込まれたsort.Interface値のLessメソッドを呼び出し、インデックスが逆になっており、ソート結果を逆順にする。
  • 異なる列でソートするためには新たな方を定義する必要がある

  • Golangのsortパッケージ

http.Handler インタフェース

package http

type Handler interface {
    ServeHTTP(w ResponseWriter, r *Request)
}

func ListenAndServe(address string, h Handler) error
  • ListenAndServe関数は”localhost:8000”などのサーバのアドレス、およびすべてのリクエストをディスパッチするHandlerインタフェースのインスタンスを必要とする。サーバが失敗すれば必ずnilではないerrorを返す。

型アサーション

型アサーション(type assertion)はインスタンス値へ提供される演算です。

型アサーションの構文

x.(T)

xがインタフェース型の式であり、Tは断定型と呼ばれる型である。

断定されている方に関係なく、オペランドがnilのインタフェース値であれば、型アサーションは失敗する。