Go言語のキャッチアップ その1

データとメソッドの定義場所が異なる

Rubyでは、class ~ endで囲まれた領域に、インスタンス変数へのアクセサやメソッドをまとめて定義できる。

class Item
  attr_accessor :name

  def label
    @name
  end
end # ここまでがクラスの定義
// Item構造体。データの定義。
type Item struct {
  Name string // 構造体が持つフィールド
} // 定義はここで終了

func (i Item) Label() string {
  return i.Name
} // Item構造体の外

(i Item)はレシーバーの宣言であり、LabelをItem型のメソッドとして定義する役割を持つ。 つまりRubyではクラスの内側にメソッドを記述するのに対し、Goでは構造体の外側にメソッドを記述し、レシーバーによって型と関連付ける。

interfaceによって多態性(ポリモーフィズム)を実現する

Rubyの場合

Rubyでは、ダックタイピングによって多態性(ポリモーフィズム)を簡単に実現できる。

class Dog
  def speak
    puts "bow!"
  end
end

class Cat
  def speak
    puts "meow!"
  end
end

# 引数のオブジェクトに対してspeakを呼び出す
def introduce(animal)
  animal.speak
end

introduce(Dog.new) # bow!
introduce(Cat.new) # meow!

introduceは、引数のオブジェクトがどのクラスに属しているかを意識せず、speakを呼び出している。 つまり、異なるクラスのオブジェクトでも、共通のメソッドを持っていれば同じように扱える。これがダックタイピングによる多態性である。

Goの場合

Goの場合interfaceを利用してポリモーフィズムを表現する。

// 共通の振る舞いを定義
type Speaker interface {
    Speak()
}

type Dog struct{}

func (d Dog) Speak() {
    fmt.Println("bow!")
}

type Cat struct{}

func (c Cat) Speak() {
    fmt.Println("meow!")
}

// Speakerを満たす型なら受け取れる
func introduce(animal Speaker) {
    animal.Speak()
}

func main() {
    introduce(Dog{}) // bow!
    introduce(Cat{}) // meow!
}

Goのinterfaceは、型が満たすべき振る舞い(メソッド)を定義する仕組みである。 interfaceは、どのようなメソッドを持つべきかを定義するだけで、そのメソッドの具体的な処理内容までは規定しない。 具体的な処理は、それぞれの型に関連付けられたメソッドで実装する。 そのため、異なる型でも同じinterfaceを満たすことができ、共通のメソッド呼び出しに対して型ごとに異なる振る舞いを実現できる。

interfaceは暗黙的に満たされる

Goでは、ある型がinterfaceを満たすことを明示的に宣言する必要はない。

type Speaker interface {
    Speak()
}

type Dog struct{}

func (d Dog) Speak() {
    fmt.Println("bow!")
}

DogはSpeakerの存在を意識していないが、Speak()メソッドを持っているため、自動的にSpeakerを満たす。 つまり、Speakerが要求するメソッドを持つ型であれば、Speakerとして扱える。 ただし、メソッド名だけでなく、引数や戻り値を含むシグネチャが一致している必要がある。 この仕組みを**暗黙的なインターフェースの実装(Implicit Interface Implementation)**と呼ぶ。

まとめ

RubyとGoでは、データとメソッドの定義方法や、多態性の実現方法が異なる。

  • データとメソッドの定義:Rubyではクラスの内部にメソッドを定義するが、Goでは構造体の外部にメソッドを定義し、レシーバーによって型と関連付ける。
  • 多態性の実現:Rubyではダックタイピングによって多態性を実現できる。一方、Goではinterfaceを利用することで、異なる型を共通のインターフェース型として扱える。
  • interfaceの役割:型が持つべきメソッドを定義するものであり、具体的な処理内容までは規定しない。
  • 暗黙的な実装:Goではinterfaceを満たすことを明示的に宣言する必要はなく、要求されるメソッドを持つ型は自動的にそのinterfaceを満たす。

GoにはRubyのようなクラスは存在しないが、struct、レシーバー、interfaceを組み合わせることで、データと振る舞いを関連付け、多態性を実現できる。

今日は一旦ここまで!!