Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the practical usage of an array of an unbounded wildcard parameterized type?

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.

  1. What is practical usage of unbounded wild-card parameterized type? It's clearly evident from the example, that you can add elements to the array but while retrieving, it issues a compiler error?
  2. What does the article mean, when it states that, these wild-card parameterized are only acceptable when superior efficiency of arrays is of paramount importance?

Can someone elaborbate on this issue?

like image 446
benz Avatar asked Aug 19 '13 09:08

benz


People also ask

What is a wildcard parameterized type?

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.

What is an unbounded wildcard?

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.

What is bounded and unbounded wildcards in generics Java?

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.


1 Answers

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.

like image 94
anubhava Avatar answered Oct 21 '22 11:10

anubhava