Before generics came in, we had to cast every object that you read from a collection.
private static void integersTest() {
List numbers = Arrays.asList(1,2,3,4);
for(Object number: numbers) {
Integer numberAsInteger = (Integer)number;
System.out.print(numberAsInteger.intValue() + “ “);
}System.out.println(“\nIntegers with generics..”);
List<Integer> integers = Arrays.asList(1,2,3,4);
for(Integer integer: integers) {
System.out.print(integer + “ “);}
}
Definitely feels a lot more comfortable to not have to deal with casting operations.
Compile time type safety
Generics give you compile time type safety which is not present in code before generics came in which works off of class casting idioms.
Below example has a string added to a list semantically meant to hold integers. When the print method tries to consume the content as an Integer, fails when the rogue string is encountered. This amounts to a production issue that needs a ramble and scramble kind of fixing.
private static void runTimeIssueDemo() {
List numbers = new ArrayList(Arrays.asList(1,2,3,4));
numbers.add(“Five”);
for(Object number: numbers) {
Integer numberAsInteger = (Integer)number;
System.out.print(numberAsInteger.intValue() + “ “);
}
}
With generics, you get compile time type safety and compiler catches the error when you try to add a string to an integer collection which means you don’t get to deploy wrong code into production thus preempting the run time issue that happened in the previous scenario.
private static void compileTimeTypeSafetyDemo() {
List<Integer> numbers = new ArrayList(Arrays.asList(1,2));
numbers.add(“Five”);
// Compiler error: The method add(Integer) in the type List<Integer> is not applicable for the arguments (String)
}
What is a Generic class?
A class whose definition has one or more type parameters is referred to as a generic class.
e.g. ArrayList<Integer>
What is a Generic interface?
An interface whose definition has one ore more type parameters is referred to as a generic interface.
e.g. List<E>
What are Generic types?
Generic interfaces and generic types are together referred to as generic types.
What is a parameterized type?
A generic type whose type is specified as a parameter is referred to as a parameterized type.
e.g. List<String> is a parameterized type.
How are generic types and parameterized types related?
Generic type corresponds to a set of parameterized types. That is given one generic type, you can define multiple parameterized types based on the types that you pass on to the generic type.
List<String> and List<Integer> are parameterized types that correspond to the generic type List<E>
What is formal type parameter and actual type parameter?
Formal type parameter is the parameter used in the generic type and actual type parameter is the parameter used in the parameterized type.
For the example, List<Integer> is a parameterized type corresponding to the generic type List<E>. Here E is the formal type parameter and Integer is the actual type parameter.
What is raw type?
This is the name of generic type used without any accompanying type parameters.
e.g. In the context of the
Generic type List<E>,
one of whose parameterized types is List<Integer>
where E is the formal type parameter and
Integer is the actual type parameter,
List is the raw type.
How does raw type behave in correspondence with it’s generic type?
Raw type behaves as if all the generic type information has been erased from the type declaration.
Generic types are better than Raw types
More Expressive
More Safe
Difference between raw type List and List of objects List<Object>
The raw type can be assigned a List<Integer> but List<Object> cannot be assigned a List<Integer>. You lose type safety when using raw type.
private static void rawTypeAndListOfObject() {
List<Integer> actualNumbers = List.of(1,2);
List numbers = actualNumbers;
List<Object> integers = actualNumbers;
// Compiler error
//Type mismatch: cannot convert from List<Integer> to List<Object>
}
What are unbounded wildcard types?
Unbounded wildcard type corresponding to a generic type is the type that takes ? as the type.
Set<?> is the unbounded wildcard type corresponding to the generic type Set<E>. Set<?> is referred to as the set of some type.
It is the most general parameterized Set type, capable of holding any set.
The only thing you can add to a List<?> (List of some type) is null
private static void wildCardType() {
List<?> wild = new ArrayList<>();
wild.add(null);
wild.add(null);
wild.add(“adf”);
//The method add(capture#3-of ?) in the type
// List<capture#3-of ?> is not applicable for the arguments (String)
}
private static int countCommon(Set setOne, Set setTwo) {
int count = 0;
for(Object o: setOne) {
setTwo.add(Integer.valueOf(100));
if(setTwo.contains(o)) {
count++;
}
}
return count;
}
Right approach with wild card types
private static int countWildCardCommon(Set<?> setOne, Set<?> setTwo) {
int count = 0;
for(Object o: setOne) {
//setTwo.add(Integer.valueOf(42)); Compiler error
if(setTwo.contains(o)) {
count++;
}
}
return count;
}
When should you use raw types given that generally you should NOT use raw types?
Raw types should be used in class literals
Class<?> klass = List.class;
klass = (List<Integer>).class;
Type mismatch: cannot convert from List<Integer> to Class<?>
Raw types should be used in instanceof operators
List<Integer> numbers = List.of(1,2,3);
System.out.println(numbers instanceof List);
System.out.println(numbers instanceof List<Integer>);
Cannot perform instanceof check against parameterized type List<Integer>. Use the form List<?> instead since further generic type information will be erased at runtime
Detect the type with the raw type, but cast it to the wild card type
To check whether the instance variable is a List, use the instanceof operator against the type List. And if the instanceof operation is successful, cast the particular instance variable to wild card type to not cause a compiler warning.
if(arg instanceof Set) {
Set<?> aSet = (Set<?>)arg ;
System.out.println(“Size is “ + aSet.size());
}
What is unchecked conversion warning?
Compiler is warning the developer that the developer has written a statement that is going to cause type conversion and the developer has not specified how the type conversion needs to happen.
List<Integer> integers = new ArrayList();
// Type safety: The expression of type ArrayList needs unchecked conversion to conform to List<Integer>
To fix the warning, developer can use the diamond operator to indicate that he wanted the ArrayList to be of the same generic type as on the left hand side of the assignment expression.
List<Integer> integers = new ArrayList<>();
Alternately, the type can be explicitly specified to get rid of the unchecked conversion warning.
List<Integer> integers = new ArrayList<Integer>();
Alternately, if for whatever reason, you are unable to provide a proper code fix but you have reasons to be sure that the particular line on which the warning is thrown is not going to become a problem, then you can suppress the warning using the @SuppressWarning annotation.
@SuppressWarnings(“unchecked”)
List<Integer> integers = new ArrayList();
This would cause the original “unchecked” warning to not show up.
However, a different warning still shows up.
ArrayList is a raw type. References to generic type ArrayList<E> should be parameterized
To suppress this warning as well, the @SuppressWarning annotation would take an object with both the strings.
@SuppressWarnings({ “unchecked”, “rawtypes” })
List<Integer> integers = new ArrayList();
Why is it that you should not leave warnings unattended?
Because when a valid new warning shows up, you would miss it out in the melee of the other warning messages that you know are already there and which you think are harmless.
When is the ArrayStore exception thrown?
During run time, when an array of some type is assigned a value that it cannot take.
Object[] numbers = new Integer[4];
numbers[0] = “A string”;Exception in thread “main” java.lang.ArrayStoreException: java.lang.String
at test.GenericTests.arrayStore(GenericTests.java:18)
at test.GenericTests.main(GenericTests.java:13)
Reified
“Arrays are Reified at runtime.” What does this statement mean?
Arrays know and enforce their type at runtime. That is the meaning of Reification.
Reify meaning
Reify means cause to create.
Erasure
Erasure in the context of Generics refer to the property that the type constraints are enforced only at compile time and is erased out during runtime.
Erasure also implies that you cannot overload a method with parameters that only vary on the generic parameter in it. The reason is that at run time, the types are erased and there is no mechanism for java to differentiate at runtime which of the two generic parameter methods to invoke.
private void sort(List<Integer> numbers) {
}
private void sort(List<String> words) {
}Line 11: error: name clash: sort(List<String>) and sort(List<Integer>) have the same erasure
Is it possible to create an array of E using new E[]?
private static void genericArray() {
int[] numbers = new int[2];
E[] genericArray = new E[2];
}E cannot be resolved to a type
Fix for this compiler error is to add generic type parameter <E> to the method
private static <E> void genericArray() {
int[] numbers = new int[2];
E[] someValues = new E[2];
}Cannot create a generic array of E
Since reification and erasure follow diagonally opposite approaches to how the types are managed, arrays and generic collection types do not mix well together. While arrays are type sensitive during runtime, generic collections are stricter during compile time and is better in that it catches errors earlier.
Example of a type safe list based chooser
package test;
import java.util.*;
public class Chooser<T> {private final List<T> choices;
Chooser(Collection<T> choices) {
this.choices = new ArrayList(choices);
}public T choose() {
Random random = new Random();
return choices.get(random.nextInt(choices.size()));
}public static void main(String[] args) {
Chooser<Integer> numbers = new Chooser<>(List.of(1,2,3,4));
Integer choice = numbers.choose();
System.out.println(choice);Chooser<String> words = new Chooser<>(List.of(“Apple”,”Mango”,”Peach”));
String wordOfTheDay = words.choose();System.out.println(wordOfTheDay);
}}
2
Mango
Generic method to get union of two sets
public static void main(String[] args) {
Set<Integer> setOne = new HashSet(Arrays.asList(1,3,4,5));
Set<Integer> setTwo = new HashSet(Arrays.asList(3,4,6,7));
Set<Integer> unionSet = union(setOne, setTwo);
System.out.println(unionSet);
Set<String> wordsOne = new HashSet(Arrays.asList(“Black”,”White”));
Set<String> wordsTwo = new HashSet(Arrays.asList(“White”,”Green”));
Set<String> unionWords = union(wordsOne, wordsTwo);
System.out.println(unionWords);
}
public static <E> Set<E> union(Set<E> set1, Set<E> set2) {
Set<E> result = new HashSet<>(set1);
result.addAll(set2);
return result;
}[1, 3, 4, 5, 6, 7]
[White, Black, Green]
How to read the expression <E extends Comparable<E>>?
Any type E that can be comparable to itself.