tanish-kr's learning log

Learning output log

Go

メソッド

goのおいてのオブジェクトはメソッドをつ単なる値あるいは変数であり、メソッドは特定の方に関連付けられた関数である。

メソッド宣言

type Point struct {X, Y float64}

// 関数
func Distance(p, q Point) float64 {
    return math.Hypot(q.X-p.X, q.Y-p.Y)
}

// Calc型のメソッド
func (p Point) Distance(q Point) int {
    return math.Hypot(q.X-p.X, q.Y-p.Y)
}

func main() {
    p :- Point{1, 2}
    q :- Point{1, 2}
    fmt.Println(Distance(p, q)) // 関数呼び出し
    fmt.Println(p.Distance(q)) // メソッド呼び出し
}

パラメータはメソッドのレシーバと呼ばれる

ポインタレシーバを持つメソッド

goは通常のデータの受け渡しは、値渡しの形になるので、それだとメモリ効率は良くないので、ポインタで渡す参照渡しのやり方が以下になる

func (p *Point) ScaleBy(factor fload64) {
    p.X *= factor
    p.Y *= factor
}

func main() {
    r := &Point{1, 2}
    r.ScaleBy(2)
    fmt.Println(*r)
}

関数によっては引数としてnilポインタを許すように、メソッドによってはそのレシーバにnilを許可する。特に、マップやスライスなどのように、nilがその型で意味を持つゼロ値の場合など。

構造体埋め込みよる型の合成

import "image/color"

type Point struct{X, Y float64}

type ColoredPoint struct {
    Point // Pointの構造体を埋め込み
    Color color.RGBA
}

func main() {
    var cp ColorPoint
    cp.X // Pointの構造体を埋め込んでいるのでXフィールドが利用できる
    red := color.RGBA{255, 0, 0, 255}
    var p = ColoredPoint{Point{1, 1}, red}
    var q = ColoredPoint{Point{5, 4}, blue}
    fmt.Println(p.Distance(q.Point)) // PointのメソッドがColoredPoiintへ格上げされている
}

無名フィールドの型は名前付き型へのポインタでもよい

type ColoredPoint struct {
    *Point
    Color color.RGBA
}

func main() {
    red := color.RGBA{255, 0, 0, 255}
    p := ColoredPoint{&Point{1, 1}, red}
    q := ColoredPoint{&Point{5, 4}, blue}
    fmt.Println(p.Distance(*q.Point))
}

メソッド値とメソッド式

メソッド値

特定のレシーバ値にメソッドを結びつけている関数を生成する。その後で、この関数をレシーバ値なしで呼び出すことができる。レシーバではない引数だけを必要とする

func main() {
    p := Point{1, 2}
    q := Point{4, 6}

    distanceFromP := p.Distance // メソッド値
    fmt.Println(distanceFromP(q))
    var origin Point
    fmt.Println(distanceFromP(origin))
}

メソッド式

メソッドを呼び出す場合は、普通の関数とは違ってセレクタ構文を使う特別な方法でレシーバを提供しなければならない。メソッド式はT.fあるいは(*T).fと書き、レシーバの代わりをする普通の第一パラメータを持つ関数値を生成する。したがって普通の方法で呼び出すことができる

同じ型に属するいくつかのメソッドからの選択を表す値が必要な場合には、メソッド式が役立つ。

p := Point{1, 2}
p := Point{4, 6}

distance := Point.Distance // メソッド式
fmt.Println(distance(p, q))
fmt.Println("%T\n", distance)

ビットベクタ型

符号なし整数値のスライスを使い、その個々のビットはセット内の可能な要素を表す。

カプセル化

Goでは大文字で始まる識別子は、それが定義されたパッケージの外へ公開され、小文字の識別子は公開されない。オブジェクトをカプセル化するには、オブジェクトを構造体にしなければならない。

メリット

  • クライアントがオブジェクトの変数を直接編集できないので、オブジェクトの変数がとり得る値を理解するために調べる必要があるコードが少なくなる
  • 詳細な実装の隠蔽によりクライアントは変更されるかもしれない事柄に依存しなくなり、APIの互換性を破ることなく実装を発展させることができる
  • クライアントがオブジェクトの変数勝手に設定するのを防いでくれる