Skip to content
wannacode

What is it really?

Problem

Only some of them can charge

A company has a fleet of regular and electric cars. Every evening the electric ones need to be charged. ElectricCar inherits from Car and adds charge(), so they are all in one Car[] array:

Car[] fleet = {
new Car("Skoda"),
new ElectricCar("Tesla", 35),
new Car("Dacia"),
new ElectricCar("Nissan", 60)
};
for (Car c : fleet) {
c.charge();
}

The program does not compile:

"cannot find symbol: method charge()".

You know why from the lesson on polymorphism: the type of the variable decides what you can call. c is a Car, and Car has no charging.

A regular car really cannot be charged. You have to ask each car: "are you electric?"

The idea

Ask the object what it is

for (Car c : fleet) {
if (c instanceof ElectricCar e) {
e.charge();
System.out.println(e.getBrand() + ": charged to " + e.getBattery() + "%");
}
}
Output
Tesla: charged to 100%
Nissan: charged to 100%

c instanceof ElectricCar e does two things at once:

  • it checks whether the object c points to is an ElectricCar;
  • if it is, it creates a variable e of type ElectricCar that points to the same object.

Through e you can now call charge(). The cars stay the same. You just look at the object through a more specific type.

This is called a type check. Going from the parent type to the child type is called a downcast: down the chain Object → Car → ElectricCar.

e exists only inside the if. Outside it, the object is just a Car again.

for (Car c : fleet) {
if (c instanceof ElectricCar e) {
e.charge();
System.out.println(e.getBrand() + ": charged to " + e.getBattery() + "%");
}
}
Step by step, in words
  1. fleet is an array of Car: 2 regular and 2 electric cars.
  2. c points to Skoda: instanceof says "no". The body of the if is skipped.
  3. c points to Tesla: instanceof says "yes". e is a second arrow to the same car, and it charges it.
  4. c points to Dacia: instanceof says "no". The body of the if is skipped.
  5. c points to Nissan: instanceof says "yes". e is a second arrow to the same car, and it charges it.
  6. The cars do not change. The check only looks at the object through a more specific type.

Try it

Is it a car?

What will the program print?

Car c = new ElectricCar("Tesla", 35);
Car d = new Car("Skoda");
System.out.println(c instanceof Car);
System.out.println(c instanceof ElectricCar);
System.out.println(d instanceof ElectricCar);
Answer

The idea

Other forms you will run into

The pattern with a variable (instanceof ElectricCar e) arrived in Java 16. In older code the check and the cast are separate:

Car c = fleet[1];
if (c instanceof ElectricCar) {
ElectricCar e = (ElectricCar) c;
e.charge();
System.out.println(e.getBrand() + ": " + e.getBattery() + "%");
}
Output
Tesla: 100%

(ElectricCar) c is a cast, with the same parentheses as (double) from the "Basics" module. It tells the compiler: "trust me, there is an electric car here". If there is not, the program stops with ClassCastException. That is why a cast with no check before it is a risk.

More precisely, if you are curious
  • The check is always false for null: null instanceof Car is false. So after a successful instanceof you never have null.
  • If the two types have nothing in common (String and Car), instanceof does not compile: the compiler sees that the answer is always "no".
  • The check works with interfaces too: a instanceof Swimmable s asks whether the object's class implements the contract.

Try it

Find the bug

The program counts the regular and the electric cars. The fleet has two of each, but it prints:

Output
Regular: 4, electric: 0

Which line is the bug on?

Pick the line with the bug.

The idea

A check or a method?

You want to print what each car runs on. A type check can do it:

for (Car c : fleet) {
if (c instanceof ElectricCar) {
System.out.println(c.getBrand() + ": electricity");
} else {
System.out.println(c.getBrand() + ": gasoline");
}
}

But this is the salary with an if per role from the lesson on polymorphism. Every car has an energy source, just a different one. That is a job for a method in Car, overridden in ElectricCar:

// in Car
public String energy() {
return "gasoline";
}
// in ElectricCar
@Override
public String energy() {
return "electricity";
}
for (Car c : fleet) {
System.out.println(c.getBrand() + ": " + c.energy());
}
Output
Skoda: gasoline
Tesla: electricity
Dacia: gasoline
Nissan: electricity

Tomorrow a hybrid car comes along. With the method, you write a new class. With the checks, you hunt down every if in the program.

The rule:

  • Every object does it, each in its own way: a method in the parent, and overriding.
  • Only some objects can do it (charging, pedaling): a type check.

Try it

What is missing?

for (Car c : fleet) {
    if (c  ElectricCar e) {
        e.charge();
    }
}

In short

The four pillars of OOP

With this lesson you have finished the modules "Classes and objects" and "Inheritance and polymorphism". Object-oriented programming rests on four ideas. You already use all of them:

  • Encapsulation. The object's data is hidden, and from outside you reach it only through methods that guard the rules. Account keeps the balance private and offers only deposit() and withdraw(). Encapsulation
  • Inheritance. What is shared sits in one place, the parent, and the child adds its own. Manager is an Employee with a bonus. Inheritance
  • Polymorphism. One call, different behavior depending on the object. e.monthlyPay() in one loop works out the pay of an employee, a manager and an intern. Polymorphism
  • Abstraction. You say what the object must be able to do, and each class decides how. The abstract Shape promises an area, the Payable interface promises an amount to pay. Abstract classes, Interfaces

Exercise

What is it really?

Solve these in your editor.

1. Parking lot. A SimpleCar[] array of regular and family cars (FamilyCar inherits from SimpleCar). For each one, print its type. Watch the order of the checks.

Output
This is a SimpleCar
This is a FamilyCar
This is a SimpleCar

2. Dances. A DanceMove[] array of regular and breakdance moves. For the breakdance moves, also print the complexity. For that you need the variable from the check.

Output
moonwalk: DanceMove
windmill: BreakDanceMove, complexity 8
salsa: DanceMove

3. Fleet. Take Car and ElectricCar from the exercise in the "Inheritance" lesson. In a Car[] array, put a "Skoda" with 40 L, a "Tesla" with efficiency 5, a "Dacia" with 5 L and a "Nissan" with efficiency 4. Each one drives 100 km: the electric ones with driveElectric, the rest with drive. Then print the kilometers and the fuel or the battery.

Output
Skoda: 100 km, fuel 30 L
Tesla: 100 km, battery 80%
Not enough fuel for 100 km
Dacia: 0 km, fuel 5 L
Nissan: 100 km, battery 75%

4. Zoo. An abstract class Animal with sound(), and child classes Cat, Dog and Fish. Only Fish implements Swimmable with swim(). In a loop over the animals, call each one's sound, and for the ones that can swim, also call swim().

Output
Meow, meow!
Woof, woof!
The fish is silent.
The fish swims.

More tasks

  • Pedals. In the exercise from the "Abstract classes" lesson, garage[1].pedal() did not compile. Fix it with a type check: in the loop over Vehicle[], every vehicle starts, and the bicycle also pedals.
  • Payments by kind. From the "Interfaces" lesson: the Payable[] array holds employees and invoices. Print the total for salaries and the total for invoices separately. For Ivan, Maria and the two invoices: €5,500 in salaries and €2,140 in invoices.

Test

Check yourself

1. c instanceof ElectricCar e is true. What is e?
2. c points to an ElectricCar. What is c instanceof Car?
3. You check whether a car is a Car or an ElectricCar. In what order?
4. The program has many ifs like "if it is an ElectricCar, do one thing, otherwise do another". What does that tell you?
5. c points to a regular car. What happens at ElectricCar e = (ElectricCar) c;?

Made by Ivo Gergov as part of Evo Tech's campaign for accessible programming education ·