First about this code:
static void addElements(Object[] objArr) {
objArr[0] = new Pair<Integer,Integer>(0,0);
objArr[1] = new Pair<String,String>("",""); // should fail, but succeeds
}
Here you're passing an argument of type Object[]
to addElements
method. Hence compiler will allow you to add anything that is an Object
. Even this code will also compile:
static void addElements(Object[] objArr) {
objArr[0] = new Pair<Integer,Integer>(0,0);
objArr[1] = new Pair<String,String>("","");
objArr[2] = new Date(); // won't be a compilation error here
}
However you will get run time exceptions as generic types are compile time checks and run time casts.
Now your question about why even allow raw types in generics?
One of the reasons why it is allowed to have backward compatibility with older JVMs and also for the cases when developer of the Interface may not know all the types that can be supplied at run time. Your error-1
does need casting from raw types to specific types:
// this should compile
@SuppressWarnings("unchecked")
Pair<Integer, Integer> pair = (Pair<Integer, Integer>) intPairArr[0]; // NO error -1
EDIT:
About wildcards dilemma:
Let's take a very simple example of using unbounded wildcards:
Pair<?, ?> intPair = new Pair<Integer, Integer>(4, 9);
Object val2 = intPair.getSecond();
System.out.printf("val2: %d, isInt: %s%n", val2, (val2 instanceof Integer));
intPair.setFirst( null ); // assigning null will be allowed
It will compile & run and produce this expected output:
val2: 9, isInt: true
However this will not compile:
intPair.setSecond((Object) new Integer(10)); // compile error
intPair.setSecond(new Integer(10)); // compile error
In an unbounded wildcard
parameterized type such as Pair<?,?>
the type of the fields and the return types of the methods would be unknown
i.e. both fields would be of type ?
. The setter methods would take an argument of type ?
and the getter methods would return a ?
.
In this situation the compiler will not let you assign anything to the field or pass anything to the setter methods. The reason is that the compiler cannot make sure that the object that we are trying to pass as an argument of a set method is of the expected type, since the expected type is unknown.
In contrast, the getter methods can be invoked and it returns an object of an unknown type, which we can assign to a reference variable of type Object.
So you are right that in a way that it does restrict its usage, as evident above in the small example where values can be assigned during construction but not when you try calling setter methods.
However you can increase the usefulness of your code by using wildcards with lower bound type like this:
Pair<? super Object, ? super Object> intPair = new Pair<Object, Object>(4, 9);
Object val2 = intPair.getSecond();
System.out.printf("val2: %s, isInt: %s%n", val2, (val2 instanceof Integer));
intPair.setSecond(10);
val2 = intPair.getSecond();
System.out.printf("val2: %s, isInt: %s%n", val2, (val2 instanceof Integer));
Now this not only compiles but runs also with expected results:
val2: 9, isInt: true
val2: 10, isInt: true
About your 2nd question: I am quoting the para directly from your linked article:
By using arrays of raw types or unbounded wildcard parameterized types
we give away the static type checks that a homogenous sequence would
come with. As a result we must use explicit casts or we risk
unexpected ClassCastException s. In the case of the unbounded
wildcard parameterized type we are additionally restricted in how we
can use the array elements, because the compiler prevents certain
operations on the unbounded wildcard parameterized type. In essence,
arrays of raw types and unbounded wildcard parameterized types are
semantically very different from what we would express with an array
of a concrete wildcard parameterized type. For this reason they are
not a good workaround and only acceptable when the superior efficiency
of arrays (as compared to collections) is of paramount importance.
Author is stressing that unbounded wildcards in arrays is not a good workaround because of its restrictions and superior efficiency is only in the context of arrays vs collections
.