Week 44 — What does "type erasure" refer to?

Question of the Week #44
What does "type erasure" refer to?
4 Replies
Eric McIntyre
Eric McIntyre14mo ago
In Java, generics were implemented as a compile-time construct. It is possible to use a generic type like the following:
List<String> someList = new ArrayList<String>();
List<String> someList = new ArrayList<String>();
The compiler is then able to check the type of the list and make sure only valid (with respect to the type) elements are inserted in the list as well as it allows obtaining items without (explicit) casting.
someList.add("Hi");
//someList.add(123);//compile-time error
//someList.add(LocalDateTime.now());//compile-time error

String stringFromList = someList.get(0);//no casting necessary, returns Hi
//LocalDateTime invalid = someList.get(0);//prevented by the compiler because get() on a List<String> only returns String elements
someList.add("Hi");
//someList.add(123);//compile-time error
//someList.add(LocalDateTime.now());//compile-time error

String stringFromList = someList.get(0);//no casting necessary, returns Hi
//LocalDateTime invalid = someList.get(0);//prevented by the compiler because get() on a List<String> only returns String elements
However, this information is not present at runtime. At runtime, an ArrayList<String> object is the same as an ArrayList<Object> or an ArrayList<LocalDateTime> or similar. After compilation, the above code would be equivalent with the following:
//Don't actually write code using "raw types" like this, this is unsafe and emits warnings for a reason
List someList = new ArrayList();//the runtime doesn't know about the type
someList.add("Hi");//any type would be allowed
//someList.add(123);//adding an instance of a different type would be possible as well since the type is not checked
String stringFromList = (String)someList.get(0);//since get(0) could return any Object, a cast is added here
//LocalDateTime invalid = (LocalDateTime)someList.get(0);//it would also be possible to extract wrong types that way
//Don't actually write code using "raw types" like this, this is unsafe and emits warnings for a reason
List someList = new ArrayList();//the runtime doesn't know about the type
someList.add("Hi");//any type would be allowed
//someList.add(123);//adding an instance of a different type would be possible as well since the type is not checked
String stringFromList = (String)someList.get(0);//since get(0) could return any Object, a cast is added here
//LocalDateTime invalid = (LocalDateTime)someList.get(0);//it would also be possible to extract wrong types that way
Since no generic type information about objects is present at runtime, it is possible to insert call generic methods with invalid objects.
//DON'T DO THIS, it's unsafe
List<String> someStringList = new ArrayList<>();
List someUntypedList = someStringList;//This line of code is unsafe as it creates a reference of the original list which is not checked by the compiler
someUntypedList.add(LocalDateTime.now());//this inserts an object of the wrong type into the list
String s = someStringList.get(0);//this throws an exception at runtime because the List<String> actually contains a LocalDateTime here
//DON'T DO THIS, it's unsafe
List<String> someStringList = new ArrayList<>();
List someUntypedList = someStringList;//This line of code is unsafe as it creates a reference of the original list which is not checked by the compiler
someUntypedList.add(LocalDateTime.now());//this inserts an object of the wrong type into the list
String s = someStringList.get(0);//this throws an exception at runtime because the List<String> actually contains a LocalDateTime here
Raw types exist solely for compatibility and should not be used anywhere. Instead, it is possible to use so-called wildcard types for generic types where the actual type parameter is unknown:
List<String> someStringList = new ArrayList<>();
List<?> someUntypedList = someStringList;//Here, you are telling the compiler that the type parameter is unknown
//someUntypedList.add(LocalDateTime.now());//the compiler doesn't allow inserting any object into the list since the correct type is not known
List<String> someStringList = new ArrayList<>();
List<?> someUntypedList = someStringList;//Here, you are telling the compiler that the type parameter is unknown
//someUntypedList.add(LocalDateTime.now());//the compiler doesn't allow inserting any object into the list since the correct type is not known
Aside from that, it is also not possible to extract the actual generic type parameters from a generic object since that information is not present at runtime. For example, it is not generally possible to check whether an is a List<String>, List<Object> or a different List. It is only possible to check whether or not it is a List.
Object someObject = new ArrayList<String>();

boolean isList = someObject instanceof List<?>;//it is possible to check whether it is a list
//boolean isStringList = someObject instanceof List<String>;//compile-time error
Object someObject = new ArrayList<String>();

boolean isList = someObject instanceof List<?>;//it is possible to check whether it is a list
//boolean isStringList = someObject instanceof List<String>;//compile-time error
Eric McIntyre
Eric McIntyre14mo ago
Unlike to objects, it is possible to get the generic type parameters of fields and methods using reflection:
public class SomeClass{
public List<String> someList = new ArrayList<>();
public List<Integer> someMethod(List<LocalDate> someParameter){
return Collections.emptyList();
}
}
public class SomeClass{
public List<String> someList = new ArrayList<>();
public List<Integer> someMethod(List<LocalDate> someParameter){
return Collections.emptyList();
}
}
System.out.println(SomeClass.class.getField("someList").getGenericType());
System.out.println(SomeClass.class.getMethod("someMethod", List.class).getGenericReturnType());
System.out.println(Arrays.toString(SomeClass.class.getMethod("someMethod", List.class).getGenericParameterTypes()));
System.out.println(SomeClass.class.getField("someList").getGenericType());
System.out.println(SomeClass.class.getMethod("someMethod", List.class).getGenericReturnType());
System.out.println(Arrays.toString(SomeClass.class.getMethod("someMethod", List.class).getGenericParameterTypes()));
📖 Sample answer from dan1st
Eric McIntyre
Eric McIntyre14mo ago
Generics in java are actually totally fake, the runtime does not care about them. Type erasure refers to the java compiler resolving generic types to a common supertype, and making sure the runtime still works with those new types. An example: A generic type of <T> implicitly extends Object. Thus, using <T> like this:
class Holder<T> {
T value;
T get() { return value; }
}
Holder<String> s = new Holder<>();
s.value = "hello";
assert s.get().equals("hello");
class Holder<T> {
T value;
T get() { return value; }
}
Holder<String> s = new Holder<>();
s.value = "hello";
assert s.get().equals("hello");
compiles to:
class Holder {
Object value;
Object get() { return value; }
}
Holder s = new Holder();
s.value = "hello";
assert ((String) s.get()).equals("Hello");
class Holder {
Object value;
Object get() { return value; }
}
Holder s = new Holder();
s.value = "hello";
assert ((String) s.get()).equals("Hello");
(the cast in this case is unnecessary, but this would be about the use case) Every generic type can be erased to a common supertype, with all the usages of the generic type enjoying type safety at compile time. At runtime, the uses are still entirely safe, because the java compiler has proven that the generic types being used are fully compatible. Using typed classes in a raw manner will defeat the java compiler's ability to do this tho, and should be used sparingly, if at all. That is a story for another time tho.
⭐ Submission from 0x150
Eric McIntyre
Eric McIntyre14mo ago
Type erasure can be explained as the process of enforcing type constraints only at compile time and discarding the element type information at runtime.
Submission from shnehna
Want results from more Discord servers?
Add your server