Java Generics is a powerful feature that was introduced in Java 5. It enables developers to create type-safe objects and eliminate the need for casting. In addition, Wildcards in Generics provide even greater flexibility by allowing the use of an unknown type. In this article, we will learn how wildcards can be used to handle inheritance in generics.
Inheritance in Generics: Before we delve into Wildcards in Generics, let's understand inheritance in Generics. Consider the following code:
javaclass Student
{
....
}
class EnggStudent extends Student
{
....
}
class Test
{
public static void main(String args[])
{
Student s1 = new Student();
EnggStudent s2 = new EnggStudent();
s1 = s2;
}
}
In the above code, the parent class can inherit or make a reference to any object of its descendant child class because every EnggStudent is also a Student.
Now, let's modify the above code using collection and generics.
typescriptclass Student
{
....
}
class EnggStudent extends Student
{
....
}
class Test
{
public static void main(String args[])
{
ArrayList<Student> al1 = new ArrayList<Student>();
ArrayList<EnggStudent> al2 = new ArrayList<EnggStudent>();
al1 = al2;
}
}
In the above code, we have used a collection, ArrayList
, which is also a generic class. Two ArrayLists of Student and EnggStudent type have been created, and the reference of Student ArrayList is referred to an ArrayList of EnggStudent. However, this program throws a compiler error as ArrayList EnggStudent cannot be converted to ArrayList Student. Therefore the application of Generics failed during inheritance.
Wildcards in Generics:
Unlike arrays, different instantiations of a generic type are not compatible with each other, not even explicitly. This incompatibility may be softened by the wildcard if ?
is used as an actual type parameter.
Now let's look at the same example using Wildcards in generics.
typescriptclass Student
{
....
}
class EnggStudent extends Student
{
....
}
class Test
{
public static void main(String args[])
{
ArrayList<?> al1 = new ArrayList<>();
ArrayList<EnggStudent> al2 = new ArrayList<EnggStudent>();
al1 = al2;
}
}
The question mark ?
is known as the wildcard in generic programming. It represents an unknown type. The wildcard can be used in a variety of situations, such as the type of a parameter, field, or local variable; sometimes as a return type. Now al1
can take any type, and this program will run fine.
Upper Bounded Wildcards:
Now instead of making the ArrayList such that any type can be assigned to it, we can bound the ArrayList to only accept the Student or any descendants of the Student class. This can be done by using the extends
keyword. This is also called the Upper Bounded Wildcards.
typescriptclass Student
{
....
}
class EnggStudent extends Student
{
....
}
class Test
{
public static void main(String args[])
{
ArrayList<? extends Student> al1 = new ArrayList<>();
ArrayList<EnggStudent> al2 = new ArrayList<EnggStudent >();
al1 = al2;
}
}
Lower Bounded Wildcards:
In addition to this, we can also bound an ArrayList from the lower side using the super
keyword.
typescriptclass Student
{
....
}
class EnggStudent extends Student
{
....
}
class Test
{
public static void main(String args[])
{
ArrayList<?