Java Inheritance
- Inheritance is a Method of Code Reuse
- Class Hierarchies
- Java Inheritance Basics
- Declaring Inheritance in Java
- Inheritance and Type Casting
- Overriding Methods
- The instanceof Instruction
- Fields and Inheritance
- Constructors and Inheritance
- Nested Classes and Inheritance
- Final Classes and Inheritance
- Abstract Classes and Inheritance
Jakob Jenkov |
Java inheritance refers to the ability in Java for one class to inherit from another class. In Java this is also called extending a class. One class can extend another class and thereby inherit from that class.
When one class inherits from another class in Java, the two classes take on certain roles. The class that extends (inherits from another class) is the subclass and the class that is being extended (the class being inherited from) is the superclass . In other words, the subclass extends the superclass. Or, the subclass inherits from the superclass.
Another commonly used term for inheritance is specialization and generalization. A subclass is a specialization of a superclass, and a superclass is a generalization of one or more subclasses.
Inheritance is a Method of Code Reuse
Inheritance can be an effective method to share code between classes that have some traits in common, yet allowing the classes to have some parts that are different.
Here is diagram illustrating a class called Vehicle
, which has two subclasses called
Car
and Truck
.
The class Car and Truck inherits from the class Vehicle. |
The Vehicle
class is the superclass of Car
and Truck
. Car
and Truck
are subclasses of Vehicle
. The Vehicle
class
can contain those fields and methods that all Vehicle
s need (e.g. a license plate, owner etc.), whereas Car
and Truck
can contain
the fields and methods that are specific to Car
s and Truck
s.
Note: Some people will claim that inheritance is a way to categorize your classes based on what they are.
A Car
is a Vehicle
. A Truck
is a Vehicle
. In practice, however, that is not how you determine which superclasses
and subclasses your application needs to have. This is typically determined by how you need to work with them in
the application.
For instance, do you need to refer to Car
and Truck
objects as Vehicle
objects?
Do you need to process both Car
and Truck
objects uniformly? Then it makes sense to
have a common Vehicle
superclass for the two classes. If you never process Car
and
Truck
objects in the same way, there is no point in having a common superclass for them, except perhaps
to share code between them (to avoid writing duplicate code).
Class Hierarchies
Superclasses and subclasses form an inheritance structure which is also called a class hierarchy. At the top of the class hierarchy you have the superclasses. At the bottom of the class hierarchy you have the subclasses.
A class hierarchy may have multiple levels, meaning multiple levels of superclasses and subclasses. A subclass may itself be a superclass of other subclasses etc.
Java Inheritance Basics
When a class inherits from a superclass, it inherits parts of the superclass methods and fields. The subclass can also override (redefine) the inherited methods. Fields cannot be overridden, but can be "shadowed" in subclasses. How all this works is covered later in this text.
What is Inherited?
When a subclass extends a superclass in Java, all protected
and public
fields and methods of the
superclass are inherited by the subclass. By inherited is meant that these fields and methods are part of
of the subclass, as if the subclass had declared them itself. protected
and public
fields
can be called and referenced just like the methods declared directly in the subclass.
Fields and methods with default (package) access modifiers can be accessed by subclasses only if the subclass is located
in the same package as the superclass. Private fields and methods of the superclass can never be referenced
directly by subclasses. They can, however, be referenced indirectly via methods reachable from the subclass
(e.g default (package), protected
and public
methods).
Constructors are not inherited by subclasses, but a subclass constructor must call a constructor in the superclass. This will be explained in detail in a later section.
Java Only Supports Singular Inheritance
The Java inheritance mechanism only allows a Java class to inherit from a single superclass (singular inheritance). In some programming languages, like C++, it is possible for a subclass to inherit from multiple superclasses (multiple inheritance). Since multiple inheritance can create some weird problems, if e.g. the superclasses contain methods with the same names and parameters, multiple inheritance was left out in Java.
Declaring Inheritance in Java
In Java inheritance is declared using the extends
keyword.
You declare that one class extends another class by using the extends
keyword in the class
definition. Here is Java inheritance example using the extends
keyword:
public class Vehicle { protected String licensePlate = null; public void setLicensePlate(String license) { this.licensePlate = license; } }
public class Car extends Vehicle { int numberOfSeats = 0; public String getNumberOfSeats() { return this.numberOfSeats; } }
The Car
class in this example extends the Vehicle
class, meaning the Car
class inherits from the Vehicle
class.
Because the Car
class extends the Vehicle
class, the
protected
field licensePlate
from the Vehicle
class is inherited by the Car
class.
When the licensePlate
field is inherited, it is accessible inside a Car
instance.
The licensePlate
field is not actually being referenced from the Car
class in the code above,
but it could if we wanted to. Here is an example that shows how that could look:
public class Car extends Vehicle { int numberOfSeats = 0; public String getNumberOfSeats() { return this.numberOfSeats; } public String getLicensePlate() { return this.licensePlate; } }
The referencing happens inside the getLicensePlate()
method.
In many cases it would have made sense to place the getLicensePlate()
method in the Vehicle
class where the licensePlate
field is located. I just placed the getLicensePlate()
method
in the Car
class to show that it is possible.
Inheritance and Type Casting
It is possible to reference a subclass as an instance of one of its superclasses. For instance, using the class definitions from
the example in the previous section it is possible to reference an instance of the Car
class as an instance of the Vehicle
class. Because the Car
class
extends (inherits from) the Vehicle
class, it is also said to be a
Vehicle
.
Here is a Java code example:
Car car = new Car(); Vehicle vehicle = car;
First a Car
instance is created. Second, the Car
instance is assigned to a
variable of type Vehicle
. Now the Vehicle
variable (reference) points to the Car
instance. This is possible because the Car
class inherits from the Vehicle
class.
As you can see, it is possible to use an instance of some subclass as if it were an instance of
its superclass. That way, you don't need to know exactly what subclass the object is an
instance of. You could treat e.g. both Truck
and Car
instances as
Vehicle
instances.
The process of referencing an object of class as a different type than the class itself is called type casting. You cast an object from one type to another.
Upcasting and Downcasting
You can always cast an object of a subclass to one of its superclasses. This is referred to as upcasting (from a subclass type to a superclass type).
It may also be possible to cast an object from a superclass type to a subclass type, but only if the object really is an instance of that subclass (or an instance of a subclass of that subclass). This is referred to as downcasting (from a superclass type to a subclass type). Thus, this example of downcasting is valid:
Car car = new Car(); // upcast to Vehicle Vehicle vehicle = car; // downcast to car again Car car2 = (Car) vehicle;
However, the following downcast example is not valid. The Java compiler will accept it, but at runtime when this
code is executed the code will throw a ClassCastException
.
Truck truck = new Truck(); // upcast to Vehicle Vehicle vehicle = truck; // downcast to car again Car car = (Car) vehicle;
The Truck
object can be upcast to a Vehicle
object, but it cannot be downcast to
a Car
object later. This will result in a ClassCastException
.
Overriding Methods
In a subclass you can override (redefine) methods defined in the superclass. Here is a Java method override example:
public class Vehicle { String licensePlate = null; public void setLicensePlate(String licensePlate) { this.licensePlate = licensePlate; } }
public class Car extends Vehicle { public void setLicensePlate(String license) { this.licensePlate = license.toLowerCase(); } }
Notice how both the Vehicle
class and the Car
class defines a method
called setLicensePlate()
. Now, whenever the setLicensePlate()
method
is called on a Car object, it is the method defined in the Car class that is called. The method in the superclass
is ignored.
To override a method the method signature in the subclass must be the same as in the superclass. That means that the method definition in the subclass must have exactly the same name and the same number and type of parameters, and the parameters must be listed in the exact same sequence as in the superclass. Otherwise the method in the subclass will be considered a separate method.
In Java you cannot override private methods from a superclass. If the superclass calls a private method internally from some other method, it will continue to call that method from the superclass, even if you create a private method in the subclass with the same signature.
The @override Annotation
If you override a method in a subclass, and the method is all of a sudden removed or renamed or have its signature changed in the superclass, the method in the subclass no longer overrides the method in the superclass. But how do you know? It would be nice if the compiler could tell you that the method being overridden no longer overrides a method in the superclass, right?
This is what the Java @override
annotation is for. You place the Java @override
annotation
above the method that overrides a method in a superclass. Here is Java @override
example:
public class Car extends Vehicle { @Override public void setLicensePlate(String license) { this.licensePlate = license.toLowerCase(); } }
Calling Superclass Methods
If you override a method in a subclass, but still need to call the method defined in the superclass,
you can do so using the super
reference, like this:
public class Car extends Vehicle { public void setLicensePlate(String license) { super.setLicensePlate(license); } }
In the above code example the method setLicensePlate()
in the Car
class,
calls the setLicensePlate()
method in the Vehicle
class.
You can call superclass implementations from any method in a subclass, like above. It does not have
to be from the overridden method itself. For instance, you could also have called super.setLicensePlate()
from a method in the Car
class called updateLicensePlate()
which does not override
the setLicensePlate()
method.
The instanceof Instruction
Java contains an instruction named instanceof
. The instanceof
instruction can determine
whether a given object is an instance of some class. Here is a Java instanceof
example:
Car car = new Car(); boolean isCar = car instanceof Car;
After this code has been executed the isCar
variable will contain the value true
.
The instanceof
instruction can also be used determine if an object is a instance of a superclass
of its class. Here is an instanceof
example that checks if a Car
object is an instance
of Vehicle
:
Car car = new Car(); boolean isVehicle = car instanceof Vehicle;
Assuming that the Car
class extends (inherits from) the Vehicle
class, the isVehicle
variable will contain the value true
after this code is executed. A Car
object is also
a Vehicle
object because Car
is a subclass of Vehicle
.
As you can see, the instanceof
instruction can be used to explore the inheritance hierarchy.
The variable type used with the instanceof
instruction does not affect its outcome. Look at this
instanceof
example:
Car car = new Car(); Vehicle vehicle = car; boolean isCar = vehicle instanceof Car;
Even though the vehicle
variable is of type Vehicle
, the object it ends up pointing to
in this example is a Car
object. Therefore the vehicle instanceof Car
instruction will evaluate to
true
.
Here is the same instanceof
example, but using a Truck
object instead of a
Car
object:
Truck truck = new Truck(); Vehicle vehicle = truck; boolean isCar = vehicle instanceof Car;
After executing this code the isCar
will contain the value false
. The Truck
object is not a Car
object.
Fields and Inheritance
As mentioned earlier, in Java fields cannot be overridden in a subclass. If you define a field in a subclass with the same name as a field in the superclass, the field in the subclass will hide (shadow) the field in the superclass. If the subclass tries to access the field, it will access the field in the subclass.
If, however, the subclass calls up into a method in the superclass, and that method accesses the field with the same name as in the subclass, it is the field in the superclass that is accessed.
Here is Java inheritance example that illustrates how fields in subclasses shadow (hides) fields in superclasses:
public class Vehicle { String licensePlate = null; public void setLicensePlate(String licensePlate) { this.licensePlate = licensePlate; } public String getLicensePlate() { return licensePlate; } }
public class Car extends Vehicle { protected String licensePlate = null; @Override public void setLicensePlate(String license) { super.setLicensePlate(license); } @Override public String getLicensePlate() { return super.getLicensePlate(); } public void updateLicensePlate(String license){ this.licensePlate = license; } }
Notice how both classes have a licensePlate
field defined.
Both the Vehicle
class and Car
class has the methods
setLicensePlate()
and getLicensePlate()
. The methods
in the Car
class calls the corresponding methods in the Vehicle
class. The result is, that eventually both set of methods access the licensePlate
field in the Vehicle
class.
The updateLicensePlate()
method in the Car
class however, accesses
the licensePlate
field directly. Thus, it accesses the licensePlate
field of the Car
class. Therefore, you will not get the same result if you
call setLicensePlate()
as when you call the updateLicense()
method.
Look at the following lines of Java code:
Car car = new Car(); car.setLicensePlate("123"); car.updateLicensePlate("abc"); System.out.println("license plate: " + car.getLicensePlate());
This Java code will print out the text 123
.
The updateLicensePlate()
method sets the license plate value on the
licensePlate
field in the Car
class. The
getLicensePlate()
method, however, returns the value of the licensePlate
field in the Vehicle
class. Therefore, the value 123
which is set
as value for the licensePlate
field in the Vehicle
class via the
setLicensePlate()
method, is what is printed out.
Constructors and Inheritance
The Java inheritance mechanism does not include constructors. In other words, constructors of a superclass are not
inherited by subclasses. Subclasses can still call the constructors in the superclass using the super()
contruct. In fact, a subclass constructor is required to call one of the constructors in the superclass as the very first
action inside the constructor body. Here is how that looks:
public class Vehicle { public Vehicle() { } }
public class Car extends Vehicle{ public Car() { super(); //perform other initialization here } }
Notice the call to super()
inside the Car
constructor. This super()
call
executes the constructor in the Vehicle
class.
You may have seen Java classes where the subclass constructors did not seem to call the constructors in the superclass. Maybe the superclass did not even have a constructor. However, the subclass constructors have still called superclass constructors in those case. You just could not see it. Let me explain why:
If a class does not have any explicit constructor defined, the Java compiler inserts an implicit no-arg constructor.
Thus, a class always has a constructor. Therefore the following version of Vehicle
is equivalent to the
version shown just above:
public class Vehicle { }
Second, if a constructor does not explicitly call a constructor in the superclass, the Java compiler inserts an
implicit call to the no-arg constructor in the superclass. That means that the following version of the Car
class is actually equivalent to the version shown earlier:
public class Car extends Vehicle{ public Car() { } }
In fact, since the constructor is now empty, we could leave it out and the Java compiler would insert it, and insert an implicit call to the no-arg constructor in the superclass. This is how the two classes would look then:
public class Vehicle { }
public class Car extends Vehicle{ }
Even though no constructors are declared in these two classes, they both get a no-arg constructor, and the no-arg
constructor in the Car
class will call the no-arg constructor in the Vehicle
class.
If the Vehicle
class did not have a no-arg constructor, but had another constructor which takes parameters,
the Java compiler would complain. The Car
class would then be required to declare a constructor, and
inside that constructor call the constructor in the Vehicle
class.
Nested Classes and Inheritance
The same Java inheritance rules apply to nested classes. Nested classes which
are declared private
are not inherited. Nested classes with the default (package) access modifier
are only accessible to subclasses if the subclass is located in the same package as the superclass. Nested classes
with the protected
or public
access modifier are always inherited by subclasses.
Here is a nested class inheritance example:
class MyClass { class MyNestedClass { } }
public class MySubclass extends MyClass { public static void main(String[] args) { MySubclass subclass = new MySubclass(); MyNestedClass nested = subclass.new MyNestedClass(); } }
Notice how it is possible to create an instance of the nested class MyNestedClass
which is defined
in the superclass (MyClass
) via a reference to the subclass (MySubclass
).
Final Classes and Inheritance
A class can be declared final
. Here is now that looks:
public final class MyClass { }
A final
class cannot be extended. In other words, you cannot inherit from a final
class
in Java.
Abstract Classes and Inheritance
In Java a class can be declared abstract
. I have explained abstract
classes in more
detail in my Java abstract classes tutorial.
An abstract
class is a class that does not
contain the full implementation of whatever the abstract
class should do. Thus, it cannot be
instantiated. In other words, you cannot create objects of an abstract
class.
In Java abstract
classes are intended to be extended to create a full implementation. Thus, it is
fully possible to extend an abstract
class. The Java inheritance rules are the same for abstract
classes as for non-abstract classes.
Tweet | |
Jakob Jenkov |