技術向上

プログラミングの学び、気になるテクノロジーやビジネストレンドを発信

コンストラクタ【Go】

Javaなど他言語に存在するコンストラクタは、Goには存在しません。
それでも、オブジェクト指向ライクを実現するための慣習があります。

コンストラクタは、初期化処理ですが、goの場合、typeに関する初期化処理は、
typeで定義したstructの中身の頭文字が、大文字である場合、次のように書くことができます。

type Vertex struct {
    X, Y int
}

func main() {
    _  := Vertex{5, 8}
}


しかし、typeで定義したstructの中身の頭文字が、小文字の場合、
外部packageからは参照することができません。
(structの型名の頭文字も同様です。) これはtypeに限らず、変数やメソッドなどにも当てはまることで、
頭文字が小文字の場合、packageの中でしか使うことができない、いわばprivateの扱いになります。

そこで、外部には隠蔽されるtypeへのアクセス方法として、コンストラクタ風の慣習が用いられます。

package A
type Vertex struct {
    x, y int
}

//

package main
import "./A"

func NewVertex(x, y int) *Vertex {    // コンストラクタ風の、ポインタを返す関数を定義
    return &Vertex{x, y}    // アドレスを返す
}

func main() {
    v := NewVertex(2, 3)    // Vertexにアクセスできる
    fmt.Println(*v)    // {2 3}   vはポインタ型なので、*を付けて実体に直す
}

New<type定義した型名>で、その型のポインタを返す関数を定義するのが慣習となっています。
ポインタで返すことで、一度変数に代入しなくてもメソッドを繋げることができます(メソッドチェーン)。
また、大きな構造体となると、値ではなく、ポインタの方がメモリの節約になります。

メソッドチェーンの例は次の通りです。

package A
type Vertex struct {
    x, y int
}

//

package main
import "./A"

func NewVertex(x, y int) *Vertex {
    return &Vertex{x, y}
}

func (v *Vertex) Scale(rate int) Vertex {
    return Vertex{v.x * rate, v.y * rate}
}

func main() {
    v := NewVertex(2, 3).Scale(5)    // メソッドチェーン。NewVertex()が値を返す場合、エラーになる
    fmt.Println(v)    // {10 15}
}


intをtype定義する場合の例です。

package A
type num int

//

package main
import "./A"

func NewNum(x num) *num {
    return &x    // アドレスを返す
}

func (n *num) scale(x num) {
    *n = *n * x    // nはポインタ型なので、*をつけて実体に直す
}

func main() {
    n := NewNum(2)
    n.scale(*n)   // nはポインタ型なので、*をつけて実体に直す
    fmt.Println(*n)   // 4  nはポインタ型なので、*をつけて実体に直す
}


mapをtype定義する場合の例です。

package A
type forMap map[string]string

//

package main
import "./A"

func NewForMap() *forMap {
    m := make(forMap, 2)
    m["国名"] = "不明"
    m["地域"] = "不明"
    return &m
}

func (fm *forMap) Asign(x, y string) {
    if fm == nil {    // ポインタレシーバのメソッドは、 nil ポインタ変数からでも呼び出しが可能なため、panicを引き起こさないよう回避する
        return
    }
    (*fm)["国名"] = x    // ポインタ型fmの実体にアクセスする
    (*fm)["地域"] = y    // ポインタ型fmの実体にアクセスする
}

func main() {
    m := NewForMap()
    fmt.Println(*m)    // map[国名:不明 地域:不明]
    m.Asign("アメリカ", "北米")
    fmt.Println(*m)    // map[国名:アメリカ 地域:北米]
}


参考
stackoverflow.com

stackoverflow.com

www.slideshare.net
※コンストラクタについて
【Java】 コンストラクタって何? this( )の意味 | 一番かんたんなJava入門