7

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?

4

2 回答 2

4

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.

于 2013-08-19T09:22:38.270 回答
0

当您需要灵活的泛型类型(例如接受 a 时Collection)但实际上并不关心类型是什么(您只是打印它toString)时,无界通配符基本上是语法糖。您可以使用泛型方法做同样的事情,但它会变得笨拙并且不会给您带来任何好处。无界通配符可让您省去它。

关于数组的解释只是提醒您,在大多数情况下,使用集合类型比使用数组更好,除非您真的负担不起集合的额外成本。在您的示例中,您发现了原因:当您将数组作为 a 传递时Object[],您是在告诉编译器可以将任何对象添加到该数组中,并且由于Pair<Integer,Integer>是 a Object,编译器允许您这样做。根据您的其他代码(您还没有完全触发异常),您现在可能在intPairArr.

于 2013-08-19T09:24:33.310 回答