Hi! In previous lessons, we've already briefly become acquainted with the concept of inheritance. Today, we'll touch on this topic again, but again not too deeply. We are still going to have a more detailed lesson on this in the future. Today we'll just take a quick look at a few practical examples and get acquainted with an interesting operator in Java.

Inheritance

So, what is inheritance? instanceof and Inheritance 101  - 1 Inheritance is a programming mechanism (including in Java) that lets you declare a new class based on an existing one. The derived class then gains access to the fields and methods of the parent class. Why would we need this? Well, imagine that you need to create several car classes in a program: Truck, RaceCar, Sedan, Pickup, etc. Even before writing any code, you know for sure that all these classes have a lot in common: all cars have a model name, year of manufacture, engine size, maximum speed, etc. (not to mention the fact that they all have wheels and other parts in common). In this situation, you can:
  • Create these fields in each class (adding them to each new car class as you create it)
  • Bring the fields common to all cars into a Car parent class, and then use the keyword extends to derive all classes for specific types of cars from the Car class.
Naturally, the second option is much more convenient:
public class Car {

   private String model;
   private int maxSpeed;
   private int yearOfManufacture;

   public Car(String model, int maxSpeed, int yearOfManufacture) {
       this.model = model;
       this.maxSpeed = maxSpeed;
       this.yearOfManufacture = yearOfManufacture;
   }
}

public class Truck extends Car {

   public Truck(String model, int maxSpeed, int yearOfManufacture) {
       super(model, maxSpeed, yearOfManufacture);
   }
}

public class Sedan extends Car {
   public Sedan(String model, int maxSpeed, int yearOfManufacture) {
       super(model, maxSpeed, yearOfManufacture);
   }
}
At a minimum, we avoid the unnecessary duplication of code (and we should always strive for that when writing programs). Plus, we have a simple and understandable class structure, with all fields common to all cars consolidated into one class. If trucks have any special fields that other cars do not, they can be declared in the Truck class. The same goes for methods. All cars have certain common behavior that can be described with methods, e.g. start the car, accelerate/brake, etc. These common methods can be consolidated into the Car parent class, and each specific type of car can define its unique actions in their derived classes.
public class Car {

   public void gas() {
       // Accelerate
   }

   public void brake() {
       // Brake
   }
}


public class F1Car extends Car {

   public void pitStop() {

       // Only race cars make pit stops
   }

   public static void main(String[] args) {

       F1Car formula1Car = new F1Car();
       formula1Car.gas();
       formula1Car.pitStop();
       formula1Car.brake();
   }
}
We added the methods common to all cars to the Car class. But, look at the F1Car class, which represents "Formula 1" race cars. Pit stops (stops for urgent car maintenance) are only done in races, so we added this specific functionality to the relevant derived class. instanceof and Inheritance 101  - 2

instanceof operator

In Java, there is a special operator, instanceof, for checking to see if an object was created based on a particular class. It returns true or false depending on the result of the check. Let's see how it works using the classes in our car example:
public class Truck extends Car {

   public static void main(String[] args) {

       Truck truck = new Truck();
       System.out.println(truck instanceof Car);
   }
}
Output: true The instanceof operator returns true, since we have a Truck object, and all trucks are cars. The Truck class is derived from the Car class. All trucks are created based on the common parent, the Car class. Look closely at how the instanceof operator is used. You write it without a period, since it's an operator, not a method ("object instanceof Class”). Let's try another way:
public static void main(String[] args) {

   Car car = new Car();
   System.out.println(car instanceof Truck);
}
Output: false The Car class (and car objects) does not derive from the Truck class. All trucks are cars, but not all cars are trucks. Car objects are not based on the Truck class. One more example:
public static void main(String[] args) {

   Car car = new Car();
   Truck truck = new Truck();
   System.out.println(car instanceof Object && truck instanceof Object);
}
Output: True Here the logic is simple too: all classes in Java, including classes you create, descend from the Object class (even though you don't write "extends Object"—it's already implied). How and when would this be useful? The instanceof operator is most commonly used when overriding the equals() method. For example, here's how the equals method is implemented in the String class:
public boolean equals(Object anObject) {
   if (this == anObject) {
       return true;
   }
   if (anObject instanceof String) {
       String anotherString = (String) anObject;
       int n = value.length;
       if (n == anotherString.value.length) {
           char v1[] = value;
           char v2[] = anotherString.value;
           int i = 0;
           while (n-- != 0) {
               if (v1[i] != v2[i])
                       return false;
               i++;
           }
           return true;
       }
   }
   return false;
}
Before comparing a String to the passed object, the method tests to see whether the object is even a string? Only then does it start comparing the two objects' properties. If this test didn't exist, any object with value and length fields could be passed to the method and compared with a String, which would be wrong, of course.