I am reading through AngelikaLangerParametrizedTypeWorkAround. I do understand many of the concepts here, i do understand what is unbounded wild card parametrized type. Though quoting from the reference it states that :-
static void test() {
Pair<?,?>[] intPairArr = new Pair<?,?>[10] ;
addElements(intPairArr);
Pair<Integer,Integer> pair = intPairArr[1]; // error -1
Integer i = pair.getFirst();
pair.setSecond(i);
}
static void addElements(Object[] objArr) {
objArr[0] = new Pair<Integer,Integer>(0,0);
objArr[1] = new Pair<String,String>("",""); // should fail, but succeeds
}
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.
I have two specific questions here.
Can someone elaborbate on this issue?
In the Java programming language, the wildcard ? is a special kind of type argument that controls the type safety of the use of generic (parameterized) types. It can be used in variable declarations and instantiations as well as in method definitions, but not in the definition of a generic type.
An unbounded wildcard is the one which enables the usage of all the subtypes of an unknown type i.e. any type (Object) is accepted as typed-parameter. For example, if want to accept an ArrayList of object type as a parameter, you just need to declare an unbounded wildcard.
Bounded and unbounded wildcards in Generics are two types of wildcards available on Java. Any Type can be bounded either upper or lower of the class hierarchy in Generics by using bounded wildcards.
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
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
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With