Overloading

We can define a become_older method that accepts a number indicating the years to grow:

  1. class Person
  2. getter :age
  3. def initialize(@name : String, @age : Int = 0)
  4. end
  5. def become_older
  6. @age += 1
  7. end
  8. def become_older(years)
  9. @age += years
  10. end
  11. end
  12. john = Person.new "John"
  13. john.age # => 0
  14. john.become_older
  15. john.age # => 1
  16. john.become_older 5
  17. john.age # => 6

That is, you can have different methods with the same name and different number of parameters and they will be considered as separate methods. This is called method overloading.

Methods overload by several criteria:

  • The number of parameters
  • The type restrictions applied to parameters
  • The names of required named parameters
  • Whether the method accepts a block or not

For example, we can define four different become_older methods:

  1. class Person
  2. @age = 0
  3. # Increases age by one
  4. def become_older
  5. @age += 1
  6. end
  7. # Increases age by the given number of years
  8. def become_older(years : Int32)
  9. @age += years
  10. end
  11. # Increases age by the given number of years, as a String
  12. def become_older(years : String)
  13. @age += years.to_i
  14. end
  15. # Yields the current age of this person and increases
  16. # its age by the value returned by the block
  17. def become_older(&)
  18. @age += yield @age
  19. end
  20. end
  21. person = Person.new "John"
  22. person.become_older
  23. person.age # => 1
  24. person.become_older 5
  25. person.age # => 6
  26. person.become_older "12"
  27. person.age # => 18
  28. person.become_older do |current_age|
  29. current_age < 20 ? 10 : 30
  30. end
  31. person.age # => 28

Note that in the case of the method that yields, the compiler figured this out because there’s a yield expression. To make this more explicit, you can add a dummy &block parameter at the end:

  1. class Person
  2. @age = 0
  3. def become_older(&block)
  4. @age += yield @age
  5. end
  6. end

In generated documentation the dummy &block method will always appear, regardless of you writing it or not.

Given the same number of parameters, the compiler will try to sort them by leaving the less restrictive ones to the end:

  1. class Person
  2. @age = 0
  3. # First, this method is defined
  4. def become_older(age)
  5. @age += age
  6. end
  7. # Since "String" is more restrictive than no restriction
  8. # at all, the compiler puts this method before the previous
  9. # one when considering which overload matches.
  10. def become_older(age : String)
  11. @age += age.to_i
  12. end
  13. end
  14. person = Person.new "John"
  15. # Invokes the first definition
  16. person.become_older 20
  17. # Invokes the second definition
  18. person.become_older "12"

However, the compiler cannot always figure out the order because there isn’t always a total ordering, so it’s always better to put less restrictive methods at the end.