Go
オブジェクト指向
golang

クラスとオブジェクトの関係性(Go 言語編)

面白い記事を見つけたので。

これを Go 言語に置き換えて語り直してみます。なお,私の個人ブログでもオブジェクトについて解説しているので,こちらも参考にどうぞ。


たい焼きの型(クラス)を作ってみる

「クラス」とは 名前,属性,操作 の3つの情報を持つ構造を定義したものです。「クラスとオブジェクトの関係性」の「たい焼き」クラスを例にすると

  • 名前:
    • Taiyaki
  • 属性:(なし)
  • 操作:
    • atama
    • shippo

という構造を指します。言い方を変えるなら上記の構造を記述できるものであればクラスとして定義できます。

ちなみに,ひとつのクラスに対してユニークな名前が1つのみ必ず存在します。属性と操作はそれぞれゼロ個以上存在します。

クラスとオブジェクトの関係性」では Ruby を使って記述していますが, Ruby にはクラスを定義するためのキーワード class が存在します。

class Taiyaki
    def atama
        puts "たい焼きの頭の方にはあんこがいっぱい入っている"
    end

    def shippo
        puts "たい焼きの尻尾にはあんこがほとんど入っていない"
        puts "しかしカリカリしていて美味しい"
    end
end

一方, Go 言語には Ruby で言うところの class に相当する構文は存在しません。その代わり Go 言語ではキーワード type を使った型宣言を使います。

type Taiyaki struct{}

func (t Taiyaki) Atama() {
    fmt.Println("たい焼きの頭の方にはあんこがいっぱい入っている")
}

func (t Taiyaki) Shippo() {
    fmt.Println("たい焼きの尻尾にはあんこがほとんど入っていない")
    fmt.Println("しかしカリカリしていて美味しい")
}

このように type 構文によって「型」を定義します。 type の名前がそのままクラスの名前に相当します。 Taiyaki 型 a.k.a Taiyaki クラスには属性がないため,空の構造体 struct{} を当てています。

func (t Taiyaki) Atama() {} の部分は関数定義です。このうち (t Taiyaki) の部分は「メソッド・レシーバ」と呼ばれるもので, Taiyaki クラスに対して Atama 関数を「操作」として関連付ける働きがあります。

たい焼き(インスタンス)を作ってみる

「インスタンス(instance)」は実体とか実例とか訳されるもので,定義であるクラスから生成した実体・実例がインスタンスというわけです。

厳密にはクラスとは「クラス・オブジェクト(class object)」で,インスタンスとは「インスタンス・オブジェクト(instance object)」です。両者とも「オブジェクト」ですが意味が異なるということですね。

さて「クラスとオブジェクトの関係性」によると Ruby におけるインスタンス生成は以下のように記述するようです。

taiyakikun_1gou = Taiyaki.new
taiyakikun_2gou = Taiyaki.new

このように,ひとつのクラスに対していくつでもインスタンスを生成できます。

一方, Go 言語でインスタンスを生成する方法はいくつかあるのですが,一番簡単なのは var キーワードによる宣言構文です。

var taiyakikun1gou Taiyaki
var taiyakikun2gou Taiyaki

あるいは,まとめて

var taiyakikun1gou, taiyakikun2gou Taiyaki

とも書けます。別の記述としては new() 関数を使い

taiyakikun1gou := new(Taiyaki)
taiyakikun2gou := new(Taiyaki)

と書くこともできます。

Ruby の new はクラス・メソッドで(class 定義の内容によって)引数を含めることができ,これによってインスタンスの内部状態を初期化できます。

一方の Go 言語var:= は宣言構文です1Go 言語でインスタンス生成時に初期化するには

taiyakikun1gou := &Taiyaki{}

のようにリテラルで表現するか

func New() *Taiyaki {
    return &Taiyaki{}
}

のようにインスタンスを生成(Ruby で言うところのインスタンス化)する関数を作るのがセオリーになっています。

では,最終的な Go 言語のコードを以下に示すとしましょう。

package main

import "fmt"

type Taiyaki struct{}

func (t Taiyaki) Atama() {
    fmt.Println("たい焼きの頭の方にはあんこがいっぱい入っている")
}

func (t Taiyaki) Shippo() {
    fmt.Println("たい焼きの尻尾にはあんこがほとんど入っていない")
    fmt.Println("しかしカリカリしていて美味しい")
}

func main() {
    var taiyakikun1gou Taiyaki
    taiyakikun1gou.Atama()
    taiyakikun1gou.Shippo()

    var taiyakikun2gou Taiyaki
    taiyakikun2gou.Atama()
    taiyakikun2gou.Shippo()
}

まとめ

「たい焼き」クラスを通して class キーワードのない Go 言語でクラスやインスタンスについてちょっとだけ掘り下げて考えてみました。



  1. taiyakikun1gou := new(Taiyaki)var taiyakikun1gou = new(Taiyaki) と同義です。また new() 関数の返り値はインスタンスそのものではなく,インスタンスへの参照(ポインタ)が返ります。