Exemplify erasure vs. overloading 2 – Objects, Immutability, Switch Expressions, and Pattern Matching
By Adenike Adekola / February 20, 2021 / No Comments / Checking sub-range in the range from 0 to length, Exams of Java, Filling a long array with pseudo-random numbers, Getting integral and fractional parts from a double, Java Certifications, Returning an identity string, Returning the flooring/ceiling modulus
Type erasure and heap pollution
Have you ever seen an unchecked warning? I’m sure you did! Is one of those things common to all Java developers 🙂 They may occur at compile-time as the result of a type checking, or at runtime as a result of a cast or method call. In both cases, we talk about the fact that the compiler cannot validate the correctness of an operation that implies some parameterized types. Not every unchecked warning is dangerous, but they are cases when we have to consider and deal with them.A particular case is represented by heap pollution. If a parameterized variable of a certain type points to an object that is not of that type then we are prone to deal with a code that leads to heap pollution. A good candidate for such scenarios involves methods with varargs arguments.Check out this code:
public static <T> void listOf(List<T> list, T… ts) {
list.addAll(Arrays.asList(ts));
}
The listOf() declaration will cause this warning: Possible heap pollution from parameterized vararg type T. So, what’s happening here?The story begins when the compiler replaces the formal T… parameter into an array. After applying type erasure, the T… parameter becomes T[], and finally Object[]. Consequently, we opened a gate to possible heap pollution. But, our code just added the elements of Object[] into a List<Object>, so we are in the safe area.In other words, if you know that the body of the varargs method is not prone to generate a specific exception (for example, ClassCastException) or to use the varargs parameter in an improper operation then we can instruct the compiler to suppress these warnings. We can do it via the @SafeVarargs annotation as follows:
@SafeVarargs
public static <T> void listOf(List<T> list, T… ts) { … }
The @SafeVarargs is a hint that sustains that the annotated method will use the varargs formal parameter only in proper operations. More common, but less recommended, is to use @SuppressWarnings({“unchecked”, “varargs”}) which simply suppresses such warnings without claiming that the varargs formal parameter is not used in improper operations.Now, let’s tackle this code:
public static void main(String[] args) {
List<Integer> ints = new ArrayList<>();
Main.listOf(ints, 1, 2, 3);
Main.listsOfYeak(ints);
}
public static void listsOfYeak(List<Integer>… lists) {
Object[] listsAsArray = lists;
listsAsArray[0] = Arrays.asList(4, 5, 6);
Integer someInt = lists[0].get(0);
listsAsArray[0] = Arrays.asList(“a”, “b”, “c”);
Integer someIntYeak = lists[0].get(0); // ClassCastException
}
This time, the type erasure transforms the List<Integer>… into List[] which is a subtype of Object[]. This allows us to do the assignment: Object[] listsAsArray = lists;. But, check out the last two lines of code where we create a List<String> and we store it in listsAsArray[0]. In the last line, we try to access the first Integer from lists[0] which obviously leads to a ClassCastException. This is an improper operation of using varargs, so it is not advisable to use @SafeVarargs in this case. We should have taken the following warnings seriously:
// unchecked generic array creation for varargs parameter
// of type java.util.List<java.lang.Integer>[]
Main.listsOfYeak(ints);
// Possible heap pollution from parameterized vararg
// type java.util.List<java.lang.Integer>
public static void listsOfYeak(List<Integer>… lists) { … }
Now, that you are familiar with type erasure, let’s briefly cover polymorphic overloading.