Type Erasure Is Used in Java Generics. in The Interest of
Type Erasure Is Used in Java Generics. in The Interest of
generics?
A1. The term type erasure is used in Java generics. In the interest of
backward compatibility, robustness of generics has been sacrificed through
type erasure. Type erasure takes place at compile-time. So, after compiling List
and List, both end up as List at runtime. They are both just lists.
Java generics differ from C++ templates. Java generics (at least until JDK 8),
generate only one compiled version of a generic class or method regardless of
the number of types used. During compile-time, all the parametrized type
information within the angle brackets are erased and the compiled class file
will look similar to code written prior to JDK 5.0. In other words, Java does
not support runtime generics.
As demonstrated above, without generics you can add any type of object to a
collection. This means you would not only have to use instanceof operator,
but also have to explicitly cast any objects returned from this list. The code is
also less readable. The following code with generics is not only more readable,
but also throws a compile time error if you try to add an Integer object to list1
or a String object to list2.
Q3.
A3.
mixed bag that contains elements of all types, for example Integer, String,
Fruit, Vegetable, etc.
2) The Collection<object>: is also a heterogeneous mixture like the
plain old Collection, but not the same and can be more restrictive than a plain
old Collection discussed above. It is incorrect to think of this as the super type
for a collection of any object types.
Unlike an Object class is a super type for all objects like String, Integer, Fruit,
etc, List<Object> is not a super type for List<String>, List<Integer>,
List<Fruit>, etc. So it is illegal to do the following:
//legal
Q4.
1) <Number>
2) <? extends Number>
3) <? super Number>
A4.
Many developers struggle with the wild cards. Here is the guide:
1. Use the ? extends wildcard if you need to retrieve object from a data
structure. That is read only. You cant add elements to the collection.
2. Use the ? super wildcard if you need to put objects in a data structure.
3. If you need to do both read and add elements, dont use any wildcard.
Q5.
1
2 List<String> list1 = new ArrayList<String>( );
3 List<Integer> list2 = new ArrayList<Integer>( );
4 System.out.println(list1.getClass( ) == list2.getClass( ));
5
Q6.
Is the following line legal? If not, how will you fix it?
1
2 List<Object> list = new ArrayList<Integer>( );
3
A6. It is Illegal because Unlike an Object class is a super type for all objects
like String, Integer, Fruit, etc, List&l;tObject> is not a super type for
List<String>, List<Integer>, List<Fruit>, etc. List<?> is the super type.
1
2 List<?> list = new ArrayList<Integer>( );
//legal
3 List<? extends Number> list = new ArrayList<Integer>( );
//legal
4
Note:<? extends Number> is read only and <?> is almost read only allowing
only removce( ) and clear ( ) operations.
The Collection<?> can only be used as a reference type, and you cannot
instantiate it.
Q7.
A7
1
2 public class MyGenericClass<T> {
3
4
T objType;
5
6
public MyGenericClass(T type) {
7
this.objType = type;
8
}
9
1
0
1
1
1
2
1
3
1
4
1
5
1
6
1
7
1
8
1
9
2
0
2
1
}
2
2
2
3
2
4
2
5
2
6
public T getObjType( ) {
return objType;
}
public void setObjType(T objType) {
this.objType = objType;
}
public static void main(String[ ] args) {
MyGenericClass<Integer> val1 = new MyGenericClass<Integer>(37);
MyGenericClass<Long> val2 = new MyGenericClass<Long>(250L);
long result = val1.getObjType( ).longValue( ) +
val2.getObjType( ).longValue( );
System.out.println(result);
}
1
4
1
5
1
6
1
7
public static void main(String[ ] args) {
1
MyGenericClass val1 = new MyGenericClass(Integer.valueOf(37));
8
MyGenericClass val2 = new MyGenericClass(Long.valueOf(250L));
1
long result = ((Integer)val1.getObjType( )).longValue( ) +
9
((Long)val2.getObjType( )).longValue( );
2
System.out.println(result);
0
}
2
}
1
2
2
2
3
2
4
2
5
//auto-box
//auto-box
If you closely examine the above code, you would notice that the compiler has
performed auto-boxing as generics does not support primitive types. The angle
brackets have been removed for val1 & val2 declarations and appropriate
castings have been added to convert from type T to Integer and Long types.
1
2 //T is inferred as an Integer
3 MyGenericClass<Integer> val1 = new MyGenericClass<Integer>(37);
4 //T is inferred as a Long
5 MyGenericClass<Long> val2 = new MyGenericClass<Long>(250L);
6
1
2
3
4
5
6
7
8
9
1
0
1
1
1
2
1
3
1
4
1
5
1
6
1
7
1
8
1
9
2
0
2
1
2
2
2
3
2
4
2
5
2
6
2
7
2
8
2
9
3
0
3
1
3
import java.util.ArrayList;
import java.util.List;
public class MyBasket {
/**
* The 'src' is the inferred type T or its sub type and the 'dest' is the
* inferred type T or its super type.
*/
public static <T> void copy(List<? extends T> src, List<? super T> dest) {
for (T obj : src) {
dest.add(obj);
}
}
public static void main(String[] args) {
List<Orange> orangeBasket = new ArrayList<Orange>(10);
List<Mango> mangoBasket = new ArrayList<Mango>(10);
orangeBasket.add(new Orange());
mangoBasket.add(new Mango());
List<Fruit> fruitBasket = new ArrayList<Fruit>(10);
List<Orange> orangeBasket2 = new ArrayList<Orange>(10);
orangeBasket2.add(new Orange());
List<Mango> mangoBasket2 = new ArrayList<Mango>(10);
mangoBasket2.add(new Mango());
List<Fruit> fruitBasket2 = new ArrayList<Fruit>(10);
fruitBasket2.add(new Mango());
MyBasket.copy(orangeBasket2, orangeBasket);
MyBasket.copy(mangoBasket2, mangoBasket);
// T is an Orange
// T is a Mango
// T is a Fruit
2
3
3
3
4
3
5
3
6
3
7
3
8
3
9
4
0
4
1
4
2
4
3
4
4
4
5
The copy() method ensures that fruits from a mixed fruit basket cannot be
copied to a basket that only holds oranges or mangoes. But a mixed fruit
basket allows fruits to be copied from any basket.
Q9.
Is the following code snippet legal? If yes why and if not why not?
1
2 public MyGenericClass( ) {
3
this.objType = new T( );
4}
5
A9. It is not legal as new T( ) will cause a compile-time error. This is partially
because theres no guarantee that the target class for raw type T has a
constructor that takes zero parameters and partially due to type erasure where
the raw type T does not have any way of knowing the type of object you want
to construct at runtime.
1
2
3
4
5
6
7
8
9
1
0
1
1
1
2
1
3
1
4
1
5
1
6
1
7
1
8
1
9
2
0
2
1
2
2
2
3
import java.util.ArrayList;
import java.util.List;
public class MyGenericMethod {
//Generified method
public static <T> void addValue(T value, List<T> list){//Line A
list.add(value);
}
public static void main(String[ ] args) {
List<Integer> listIntegers = new ArrayList<Integer>( );
Integer value1 = new Integer(37);
addValue(value1, listIntegers);
//T is inferred as an Integer
System.out.println("listIntegers=" + listIntegers);
List<String> listString = new ArrayList<String>( );
String value2 = "Test";
addValue(value2, listString);
//T is inferred as a String
System.out.println("listString=" + listString);
}
}
Note: If you had used the wildcard List<?> instead of List<T> on line A, it
would not have been possible to add elements. You will get a compile-time
error. So how does the compiler know the type of T? It infers this from your
use of the method. The generated class file looks pretty much the same as the
source file without the <Integer> and <String> angle brackets
Q11.
2
3
4 public class Generics4<T> {
5
6
public <T> void doSomething(T data) {
7
System.out.println(data);
8
}
9
1
public static void main(String[ ] args) {
0
Generics4<String> g4 = new Generics4<String>( );
1
g4.doSomething(123);
1
}
1 }
2
1
3
A11. Yes, the above code snippet does compile. It demonstrates that the
type parameter in the class name and the type parameter in the method are
actually different parameters. The method signature,
1
2
3
really means,
1
2 public void doSomething(Object data)
3
Q12.
1
2
3
4
5
6
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class GenericsWithIterators {
7
8
9
1
0
1
1
1
public static void main(String[ ] args) {
2
List<Integer> listIntegers = new ArrayList<Integer>( );
1
listIntegers.add(5); //2
3
listIntegers.add(3); //3
1
4
Iterator it = listIntegers.listIterator( ); //4
1
5
while(it.hasNext( )){ //5
1
Integer i = it.next( ); //6
6
System.out.println(i); //7
1
}
7
}
1
}
8
1
9
2
0
2
1
2
2
//1
A12.
Line 4 will cause compile-time error on line 6 as the iterator is not generic. To
fix this, replace line 4 with:
1
2 Iterator<Integer> it = listIntegers.listIterator( ); // fix 1
3
1
2 Integer i = (Integer) it.next( );// fix 2
The fix 1 is preferred. When you get an iterator, keyset, or values from a
collection, assign it to an appropriate parametrized type as shown in fix 1.