Introduction

Welcome back! In this part of our Ruby Class Basics Review, we’ll dive into inheritance, a core concept in Ruby’s object-oriented programming (OOP). Inheritance enables code-sharing between classes, making our code more efficient and easier to read.

This lesson covers how inheritance works in Ruby, including attribute and method inheritance, along with the super keyword to access superclass functionality. Ready to dive in? Let’s get started!

Defining Inheritance

Inheritance in Ruby allows us to create a subclass that inherits attributes and methods from a superclass. This approach is especially useful when multiple classes share common features or behaviors.

Let’s explore with a superclass named Vehicle and a subclass named Car:

# Define the superclass 'Vehicle'
class Vehicle
  # Initialize the Vehicle with color and brand attributes
  def initialize(color, brand)
    @color = color
    @brand = brand
  end
end

# Define the subclass 'Car', inheriting from 'Vehicle'
class Car < Vehicle
  def initialize(color, brand, doors)
    # Use 'super' to call the superclass's initialize method
    super(color, brand)
    @doors = doors
  end
end

In this example, Car inherits properties from Vehicle. Ruby supports several types of inheritance, but here we’re focusing on single inheritance, where a subclass has a single superclass.

Attribute Inheritance with attr_reader

With attribute inheritance, a subclass can inherit instance variables from its superclass.

class Artist
  attr_reader :name  # Getter for @name

  def initialize(name)
    @name = name  # Superclass's attribute
  end
end

class Musician < Artist
  attr_reader :instrument  # Getter for @instrument

  def initialize(name, instrument)
    super(name)  # Inheriting superclass's attribute
    @instrument = instrument  # Subclass's own attribute
  end
end

john = Musician.new('John Lennon', 'Guitar')
puts john.name        # Output: John Lennon
puts john.instrument  # Output: Guitar

The Musician class inherits the @name attribute from Artist, while also introducing its own attribute, @instrument. Getters can be conveniently defined using attr_reader to access instance variables of each class.

Method Inheritance

Similar to attributes, method inheritance lets a subclass use methods defined in its superclass without redefining them.

In the example below, the Car class inherits the start method from the Vehicle class:

class Vehicle
  def initialize(brand)
    @brand = brand
  end

  def start
    puts "The #{@brand} is starting."
  end
end

class Car < Vehicle
  # No new methods or attributes added here
end

my_car = Car.new('BMW')
my_car.start # Output: The BMW is starting.

In this case, Car can use the start method inherited from Vehicle.

Understanding the `super` Keyword

The super keyword is essential in Ruby inheritance, allowing a subclass to call methods from its superclass. super is especially useful in method overrides or in initialize methods to ensure that superclass functionality is included in the subclass.

Here’s how super can be used to call an overridden method in the superclass and combine it with new behavior in the subclass:

class Vehicle
  def start
    "Vehicle is starting..."
  end
end

class Car < Vehicle
  def start
    "#{super} Beep! Beep!"
  end
end

my_car = Car.new
puts my_car.start # Output: Vehicle is starting... Beep! Beep!

Similarly, in initialize, super allows us to ensure that both superclass and subclass attributes are set up properly:

class ParentClass
  attr_reader :value  # Getter for @value

  def initialize(value)
    @value = value
  end
end

class ChildClass < ParentClass
  attr_reader :additional_value  # Getter for @additional_value

  def initialize(value, additional_value)
    super(value)  # Calls superclass's initialize
    @additional_value = additional_value
  end
end

child = ChildClass.new("value", "additional_value")
puts child.value            # Output: value
puts child.additional_value # Output: additional_value

In both examples, super allows the subclass to build upon the superclass’s logic, resulting in flexible and reusable code.

Using self.class for Shared Class Methods and Variables

In Ruby, self.class allows instance methods to dynamically call class methods or access shared data. For example, we can track the total number of books checked out in a library system:

class Library
  @books_checked_out = 0

  class << self
    # Defines class-level methods for shared behavior
    attr_reader :books_checked_out

    def checkout_book
      @books_checked_out ||= 0  # Initialize @books_checked_out if it's nil
      @books_checked_out += 1
    end
  end
end

class LibraryMember < Library
  def checkout
    self.class.checkout_book
    "A book has been checked out. Total checked out: #{self.class.books_checked_out}"
  end
end

member = LibraryMember.new
puts member.checkout  # Output: A book has been checked out. Total checked out: 1

another_member = LibraryMember.new
puts another_member.checkout  # Output: A book has been checked out. Total checked out: 2

In this example:

  • @books_checked_out is a class instance variable, storing data shared among all instances of the Library class.
  • class << self opens the singleton class, allowing us to define class-level methods like books_checked_out and checkout_book in a grouped and organized manner.
  • The checkout method in LibraryMember uses self.class to dynamically call class methods (checkout_book and books_checked_out) on the Library class.

This design ensures shared data is managed centrally while allowing dynamic access and modifications through instance methods.

Lesson Summary

In this lesson, we explored inheritance in Ruby, covering both attribute and method inheritance. We also discussed how the super keyword allows subclasses to access superclass methods for extending or combining behaviors. Practicing inheritance will help you write more efficient, organized, and modular Ruby code. Get ready to apply these concepts in some hands-on exercises to deepen your understanding!

Sign up
Join the 1M+ learners on CodeSignal
Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal