コンテンツにスキップ

オーバーロード

これから、何歳をとるかを数値で指定できる become_older メソッドを定義します。

class Person
  getter :age

  def initialize(@name : String, @age : Int = 0)
  end

  def become_older
    @age += 1
  end

  def become_older(years)
    @age += years
  end
end

john = Person.new "John"
john.age # => 0

john.become_older
john.age # => 1

john.become_older 5
john.age # => 6

上記からわかるように、同じ名前で引数の数が異なるメソッドを定義することが可能で、それぞれが別のメソッドとして扱われます。これをメソッドのオーバーロードと呼びます。

メソッドがオーバーロードされるための条件は次の通りです。

  • 引数の数
  • 引数に指定されている型制約
  • 必要な名前付き引数の名前が異なる
  • ブロックを受け取れるかどうか

例えば、become_older であれば、以下の4つの異なるメソッドを定義することができます。

class Person
  @age = 0

  # 1歳年をとる
  def become_older
    @age += 1
  end

  # 受け取った数値だけ年をとる
  def become_older(years : Int32)
    @age += years
  end

  # 「String 型で」受け取った数値だけ年をとる
  def become_older(years : String)
    @age += years.to_i
  end

  # 現在の年齢を yield して
  # そのブロックの戻り値にしたがって年をとる
  def become_older
    @age += yield @age
  end
end

person = Person.new "John"

person.become_older
person.age # => 1

person.become_older 5
person.age # => 6

person.become_older "12"
person.age # => 18

person.become_older do |current_age|
  current_age < 20 ? 10 : 30
end
person.age # => 28

コンパイラは yield が含まれていることを検知して、そのメソッドがブロックをとるメソッドであることを判断しています。より明示的にそのことを示したい場合は、&block という引数をダミーとして引数の最後に指定してください。

class Person
  @age = 0

  def become_older(&block)
    @age += yield @age
  end
end

ドキュメントでは、このように書いたかに関わらずに、必ず &block を引数に伴なって表示されます。

もし同じ数の引数をとるメソッドが複数ある場合、コンパイラは最も制約の少ないものが最後にくる (優先度の低い) ようにソートを行います。

class Person
  @age = 0

  # 最初にこのメソッドが定義されている
  def become_older(age)
    @age += age
  end

  # 「String」の指定は制約なしのものより制約的であるため、
  # オーバーロードの条件に合致していた場合は、
  # コンパイラはこのメソッドを最初のものより先に並べる (優先させる)
  def become_older(age : String)
    @age += age.to_i
  end
end

person = Person.new "John"

# 最初の定義が呼び出される
person.become_older 20

# 2番目の定義が呼び出される
person.become_older "12"

しかしながら、必ずしも順序関係が全順序であるとは限らないため、コンパイラが常に正しく順序を設定できるわけではありません。したがって、いつも制約の少ないメソッドを最後に書くようにすることを推奨します。