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:

 

Other Java Collections Tutorial:


About the Author:

is certified Java programmer (SCJP and SCWCD). He began programming with Java back in the days of Java 1.4 and has been passionate about it ever since. You can connect with him on Facebook and watch his Java videos on YouTube.



Add comment

   


Comments 

#3Kami2021-11-17 22:04
Thank You for your answer, Nam. I think I understand what You mean. If I'd cast an object of type Animal to List will produce ClassCastException.
Quote
#2Nam2021-11-15 15:58
Thanks for your feedback, Kami.
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.
Quote
#1Kami2021-11-15 03:57
Hello
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.
Quote