Java Generics with Subtyping and the Substitution Principle
- Details
- Written by Nam Ha Minh
- Last Updated on 14 June 2019   |   Print Email
This article helps you understand a couple of important rules with regard to generics in Java. They are subtyping and the Substitution Principle.
1. What is subtyping?
Subtyping is a key feature of object-oriented programming languages such as Java. In Java, Sis a subtype of T if S extends or implements T.
Subtyping is transitive, meaning that if R is a subtype of S, then R is also a subtype of T (T is the super type of both Sand R).
Here are some examples:
- Integer is a subtype of Number
- ArrayList<E> is a subtype of Collection<E>
- String is a subtype of Object
2. What is the Substitution Principle?
The Substitution Principlewas developed in 1987 by Barbara Liskov - an American computer scientist (hence it is also called Liskov’s Substitution Principle). This principle is then adopted in object-oriented programming languages.
The Substitution Principle states that:
A variable of a given type may be assigned a value of any subtype, and a method with a parameter of a given type may be invoked with an argument of any subtype of that type.
Here’s a couple of examples in Java:
- Subtyping in declarations:
Number num = new Integer(2000); Object obj = new String(“Hello World”);
- Subtyping in method invocation:
JPanel panel = new JPanel(); panel.add(new JTextField(20)); panel.add(new JButton(“OK”));
JPanel is a class in Swing, which has the add(Component) method, and JButton and JTextField are two subtypes of Component.
3. Java Generics with Subtyping and the Substitution Principle
A list of Number can contain Integer and Double objects (two subtypes of Number), hence the following example:
List<Number> numbers = new ArrayList<Number>(); numbers.add(2016); // auto-boxing converts primitive to new Integer(2016) numbers.add(3.14); // auto-boxing converts primitive to new Double(3.14)
Here, as you can see, the Substitution Principle is applied for the reference type (List) and object type (ArrayList). The add() method can accept any type which is subtype of Number, thus an Integer and a Double objects can be added to the collection.
However, the Substitution Principle does not work with the parameterized types, meaning that it is illegal to write:
List<Number> numbers = new ArrayList<Integer>();
We’ll get compile error “incompatible types”.
WHY is this disallowed?
The following example explains why subtyping is not allowed for the parameterized types of generics:
List<Integer> integers = new ArrayList<Integer>(); integers.add(2016); integers.add(2017); List<Number> numbers = integers; // Compile error numbers.add(1.68); System.out.println(integers); // can print [2016, 2017, 1.68]
Look at the 4th line. If that assignment is accepted by the compiler, then we can add a double number to a list of integer as shown in the last two lines, right?
That’s why the 4th line causes a compile error to early prevent wrong objects from being added to the collection afterward.
Now, you understand why the Substitution Principle does not work with generics type, right?
That also means it’s illegal to pass a collection of a subtype to a method with a parameter of the supertype. The following example illustrates this rule well:
class Animal { } class Dog extends Animal { } class Test { public void addAnimal(List<Animal> animals) { } public void test() { List<Dog> dogs = new ArrayList<Dog>(); addAnimal(dogs); // COMPILE ERROR! } }
We’ll get a compile error because inside the addAnimal() method, it’s possible to add a object of Animal to the passed-in collection which may accepts only Dog objects.
So remember these rules:
- Subtyping can be used with reference type and object type in generics declaration.
- Subtyping cannot be used with parameterized types.
Related Java Generics Tutorials:
- What are Generics in Java
- How to write generic classes and methods in Java
- Generics with extends and super Wildcards and the Get and Put Principle
Other Java Collections Tutorial:
- Java List Tutorial
- Java Set Tutorial
- Java Map Tutorial
- Java Queue Tutorial
- Java Stream API Tutorial
- Understand equals and hashCode in Java
- Understand object ordering in Java
Comments
I think it was correct. It means that the possibility of adding an object of type Animal to a List will cause issue, thus the code won't compile.
I have a question. At the end of your article there is: "We’ll get a compile error because inside the addAnimal() method, it’s possible to add a object of Animal to the passed-in collection which may accepts only Dog objects."
Should't be: "We’ll get a compile error because inside the addAnimal() method, it’s NOT possible to add a object of Animal to the passed-in collection which may accepts only Dog objects." ? Best regards.