skip to content

In Ruby, how do you make ElectricCar a subclass of Vehicle, override one of its methods, and still run Vehicle's version from the override?

level: juniorimportance: must knowfreq 78%

answer

  1. exactly one parent per class
  2. the < in the class line
  3. same name shadows the parent
  4. super returns the parent's result
  5. superclass mismatch is a TypeError

basics

~10 s

Write class ElectricCar < Vehicle. ElectricCar then inherits Vehicle's methods and constants; defining a method with the same name overrides it, and calling super inside that override runs Vehicle's version and returns its value.

solid answer

~40 s

`class ElectricCar < Vehicle` gives ElectricCar exactly one superclass, and `ElectricCar.superclass` returns `Vehicle`; a class written without `<` inherits from `Object`. ElectricCar instances respond to every Vehicle method. Defining a method with the same name in ElectricCar **overrides** it, because Ruby looks in the receiver's own class first. Inside the override, `super` calls the next method of that name further up and returns its result, so `"#{super}, battery mode"` extends the parent's behaviour instead of replacing it. If nothing above defines the method, `super` falls through to `method_missing` and raises `NoMethodError`. The parent is fixed by the first definition: reopening with `class ElectricCar < Object` raises `TypeError` (superclass mismatch).

code

ruby · 26 lines
ruby
class Vehicle
  WHEELS = 4

  def initialize(name)
    @name = name
  end

  def describe
    "#{@name} on #{WHEELS} wheels"
  end

  def start
    "ready"
  end
end

class ElectricCar < Vehicle
  def start
    "#{super}, battery mode"
  end
end

car = ElectricCar.new("Volt")
car.describe            # => "Volt on 4 wheels"
car.start               # => "ready, battery mode"
ElectricCar.superclass  # => Vehicle

go deeper

for a junior

Write the class line with < from memory, explain that the subclass inherits every instance method, and show super extending a parent method's return value.

for a middle

Explain that super finds the same-named method one level up at call time, that skipping super replaces the parent's code, and what NoMethodError from a missing parent method looks like.

for a senior

Point out the initialize override that forgets super, the TypeError on a superclass mismatch when a file reopens a class, and why Class.new(Parent) is handy in specs and factories.

for a principal

Frame subclassing as a single, fixed chain chosen at the first definition, and be ready to say when a module or composition is the better way to share behaviour than a new subclass.

## Declaring the parent with `<` A Ruby class names its **superclass** (its parent) with `<` in the `class` line: ```ruby class Vehicle def start "ready" end end class ElectricCar < Vehicle end ``` Key facts about this declaration: - A class has **exactly one** superclass. There is no syntax for listing two parents; sharing behaviour between unrelated classes is done with modules, which are a separate topic. - A class written without `<` inherits from `Object`. `Class#superclass` tells you the parent: `ElectricCar.superclass` is `Vehicle`, `Vehicle.superclass` is `Object`. - The expression after `<` can be any expression that evaluates to a class, which is why `class Point3 < Struct.new(:x, :y, :z)` works. It must be a class: a module there raises `TypeError` (superclass must be an instance of Class). - `Class.new(Vehicle) { ... }` builds the same kind of subclass without the `class` keyword; assigning it to a constant gives it a name. ## What the subclass gets Every **instance method** defined in Vehicle is callable on an ElectricCar, and constants defined in Vehicle are visible inside ElectricCar's methods. Nothing is copied: the subclass simply sits below the parent, and a method call that does not find the name in ElectricCar keeps looking in Vehicle, then in Object, and so on. Adding a method to Vehicle later makes it available on existing ElectricCar objects immediately. ## Overriding and calling up with `super` To **override** a method, define one with the same name in the subclass. Ruby finds the subclass's version first, so the parent's version no longer runs on its own: ```ruby class ElectricCar < Vehicle def start "#{super}, battery mode" end end ElectricCar.new.start # => "ready, battery mode" ``` The keyword `super` calls the method of the **same name** one level further up and evaluates to its return value, like any method call. You do not name the parent (`Vehicle.start` would call a class method, which is something else). You may call `super` anywhere in the override, before or after your own code, and more than once. If you never call it, the parent's code simply does not run for that method, which is the classic bug in an `initialize` override that forgets to set up the parent's instance variables. How the arguments reach the parent depends on how you spell the call: a bare `super` re-sends the current method's arguments, while `super()` and `super(x)` send exactly what is in the parentheses. That difference is worth studying on its own. ## Tracing one call For `ElectricCar.new("Volt").start`, Ruby does the following: 1. It looks for `start` in `ElectricCar`, the receiver's class, and finds the override. 2. The override starts building its string and reaches `super`. 3. `super` resumes the search for `start` **above** `ElectricCar`, finds `Vehicle#start`, and calls it with the same receiver, so `self` is still the ElectricCar object and its instance variables are the ones Vehicle's code sees. 4. `Vehicle#start` returns `"ready"`, and the override finishes with `"ready, battery mode"`. The point of step 3 is that `self` never changes: the parent's method runs on the child object, which is why parent code that calls another overridden method will reach the child's version. ## What fails, and how 1. **No parent method.** If neither Vehicle nor anything above it defines the name, `super` goes to `method_missing`, whose default raises `NoMethodError` with a message beginning `super: no superclass method`. 2. **Changing the parent.** Reopening a class without `<` is fine, and repeating the same parent is fine, but naming a different one raises `TypeError: superclass mismatch for class ElectricCar`. Ruby cannot re-parent an existing class. 3. **Inheriting from the wrong thing.** `class Foo < Class` raises `TypeError` (can't make subclass of Class), and a singleton class cannot be subclassed either. ## Quick reference | You write | Effect | |---|---| | `class ElectricCar < Vehicle` | one parent, inherits methods and constants | | `ElectricCar.superclass` | `Vehicle` | | `def start` in ElectricCar | overrides `Vehicle#start` for ElectricCar objects | | `super` inside `start` | runs `Vehicle#start`, returns its value | | `super` with no parent method | `NoMethodError` via `method_missing` | | reopening with another parent | `TypeError`, superclass mismatch | ## Why interviewers ask it This is the entry point to Ruby's object model. A good answer shows that you know inheritance is a single chain walked at call time, that an override replaces rather than merges unless you call `super`, and that `super` is a keyword that finds the parent method for you rather than a reference to the parent class.

  • What does super return, and do you have to call it in every override?
    `super` evaluates to whatever the parent method returns, so you can use it inside an expression. Calling it is optional: an override that never calls `super` fully replaces the parent's behaviour for that method. That is fine for pure replacements and a bug in overrides such as `initialize`, where skipping `super` leaves the parent's instance variables unset (they read as `nil`).
  • Can a Ruby class have two superclasses?
    No. The `class` line takes one expression after `<`, and `Class#superclass` returns a single class. Ruby covers the need for shared behaviour across unrelated classes with modules mixed into a class, which is a separate mechanism from subclassing.
  • How do you create the same subclass without the class keyword?
    `ElectricCar = Class.new(Vehicle) do ... end`. `Class.new` takes the superclass as its argument (defaulting to `Object`), `def` inside the block defines instance methods, and assigning the result to a constant gives the class its name.

Inheritance is a chain of offices on one corridor: a request not handled at ElectricCar's desk walks to Vehicle's office next door, and super is you walking the request there yourself before adding your own note.

saying these in an interview costs you the question

  • A Ruby class can list several parents, as in class ElectricCar < Vehicle, Battery.
  • Overriding start in ElectricCar also changes what plain Vehicle objects return.
  • super must name the parent explicitly, like Vehicle.start or Vehicle::start.
  • Reopening ElectricCar with a different superclass quietly re-parents it.
  • When the parent lacks the method, super silently returns nil.