This is one example of the array covariance plus type erasure in Java.
Array covariance
Arrays in Java are covariant: if an array can handle Integer (Integer[]
), and Integer is an Object, then this array can also handle Object, so it can be referenced as Object[]
. Since String is also an Object, things get tricky! (read further here and here).
Type Erasure
The correctness of the generics types is checked at compile-time, and then forgotten/erased at runtime. A Pair<Double, Double>
is just a Pair
at runtime. That's why if you can trick the compiler, as you did introducing the m1()
method, then you are ok at runtime. (read further here).
Your code explained
Your arr
variable is described as Pair<? extends Number, ? extends Number>[]
. But remember that variables are just a name that references some object. The actual object behind it is just an array of Pair (Pair[]
).
When you call m1()
, another variable is used to reference this very same array, but this time it's a more general variable that handle any array of objects (Object[]
).
You see that the actual array created in the main
method is compatible with both types of the variables used to reference it throughout the code.
To overcome the array covariance, you could choose to use an ArrayList
(or any other Collection you wish), but then, as you see, you would still have the generic type erasure problem on the Pair
objects. like this:
public static void main(String[] args) {
List<Pair<? extends Number, ? extends Number>> arr =
new ArrayList<Pair<? extends Number, ? extends Number>>();
m1(arr);
arr.add(new Pair<Integer, Integer>(1, 1));
...
}
static void m1(List arr) {
arr.add(new Pair<String, String>("test","test"));
}
What a maze, uh?!