6. Inheritance

The last topic we will talk about in Object Oriented Programming is the topic of inheritance.

Let's think about the following question. Are police cars and trucks the same?

It's a difficult question, because police cars and trucks are both cars, yet they both have unique features. In this case, we can say that a police car is a car, and a truck is a car.

Let's say we were going to code a Truck class and a PoliceCar class. We could define them like this:

class Truck
  def speed_up
    puts "I'm speeding up!"
  end

  def speed_down
    puts "I'm slowing down!"
  end

  def print_type
    puts "I'm a truck!"
  end
end

class PoliceCar
  def speed_up
    puts "I'm speeding up!"
  end

  def speed_down
    puts "I'm slowing down!"
  end

  def print_type
    puts "I'm a police car!"
  end
end

Notice how we have repeating code. We are violating a software engineering principle - DRY, Don't Repeat Yourself. To reduce the repetition, we can create a Car class, and make the Truck and Police classes inherit from Car.

We can make a class inherit from another class like this:

class Child < Parent
  # code here
end

In our car example, we can organize our code like this:

class Car
  def speed_up
    puts "I'm speeding up!"
  end

  def speed_down
    puts "I'm slowing down!"
  end

  def print_type
    "I'm a car!"
  end
end

class Truck < Car
  def print_type
    puts "I'm a truck!"
  end
end

class PoliceCar < Car
  def print_type
    puts "I'm a police car!"
  end
end

Truck.new.speed_up
=> I'm speeding up!

Truck.new.speed_down
=> I'm slowing down!

Truck.new.print_type
=> I'm a truck!

PoliceCar.new.print_type
=> I'm a police car!

PoliceCar.new.speed_up
=> I'm speeding up!

PoliceCar.new.speed_down
=> I'm slowing down!

Notice two things here:

When a method is called, it first looks into the class that the method was called upon to find the method. If the program finds the method, then the program will return from the method. If it cannot find the method in the class that the method was called upon, then the program will look to the parent class to find the method.

In the example above, the print_type method is declared inside Truck and also in Car. When we call print_type on an instance of a Truck class, it finds the print_type method inside of the Truck class and returns from the method, preventing the program from running the print_type method inside of the Car class.

All Ruby Objects inherit from Basic Object

Now that we've talked about inheritance, let's talk about how all objects in the Ruby language inherit from the BasicObject class.

Strings, arrays, integers, hashes - everything inherits from BasicObject. Don't believe me?

Let's open up irb and see for ourselves. We can use the superclass method to see the parent class:

String.superclass

The output will show Object. Then what is the parent class of Object?

Object.superclass
=> BasicObject

Still don't believe me? Try the same procedure with Array, Hash, Integer, or any other class. Even the Cat class we created ultimately inherits from BasicObject.

This means that we actually have access to all of the methods inside the Object class and the BasicObject class. You can take a look at all of the methods that all classes have in the documentation.

For example, we can use methods like to_s or inspect for any class, because these are methods of Object.

We can also use methods like new for every class, since those are methods of BasicObject. If you're curious, you can take a look at the methods that BasicObject has here.

Having some fun

In Ruby, we are actually allowed to rewrite the methods inside any of the default Ruby classes such as Object, String, Array, etc. Let's say we want to convert "10" into an integer with the to_i method:

"10".to_i
=> 10

The to_i method is defined inside the String class. What we can do is rewrite the to_i method inside the String class to make it do something else. Open up irb and type in the following:

class String
  def to_i
    puts "lol, you can't convert it into an integer now!"
  end
end

Now let's try to run "10".to_i:

"10".to_s
=> lol, you can't convert it into an integer now!

Just like that, we've completely rewritten the Ruby language! (Don't worry, once you exit out of irb, things will be back to normal).

Overview