What is the practical use of an array of unlimited wildcard pattern?

I am reading AngelikaLangerParametrizedTypeWorkAround . I really understand many of the concepts here, I understand what is an unlimited parametric type of wild card. Although quoting from the link, he claims 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 an unlimited wildcard wildcard type, we have an additional restriction on how we can use array elements, because the compiler prevents certain operations on an unlimited wildcard type parameter. Essentially, arrays of raw types and unlimited wildcard parameterized types are semantically very different from what we will express using an array of a specific template with type parameters. For this reason, they are not a good workaround and only acceptable when the superior performance of arrays (compared to collections) is paramount.

I have two specific questions.

  • What is the practical use of the unlimited parameterized wild-card type? As you can see from the example, you can add elements to the array, but when retrieving it gives a compiler error?
  • What does this article mean when it states that these wildcard options are acceptable only when the superior array efficiency is paramount?

Can anyone solve this problem?

+6
source share
2 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 pass an argument of type Object[] to addElements . Therefore, the compiler will allow you to add everything that is 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 runtime exceptions, since generic types are compile-time and runtime checks.

Now your question is, why even allow raw types in generics?

One of the reasons that it is allowed to have backward compatibility with older JVMs, as well as in cases where the interface designer may not know all the types that can be provided at run time. Your error-1 needs to be cast 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 the wildcards dilemma:

Let's look at a very simple example of using unlimited 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 and run and produce this expected result:

 val2: 9, isInt: true 

However, this does not compile:

 intPair.setSecond((Object) new Integer(10)); // compile error intPair.setSecond(new Integer(10)); // compile error 

In a parameterized unbounded wildcard type, such as Pair<?,?> , The field type and return method types will be unknown , that is, both fields will be of type ? . ? setter methods accept a type argument ? and getter methods return ? .

In this situation, the compiler will not allow you to assign something to a field or pass anything to setter methods. The reason is that the compiler cannot make sure that the object that we are trying to pass as an argument to the set method has the expected type, because the expected type is unknown.

In contrast, getter methods can be called and return an object of an unknown type, which we can assign to a reference variable of type Object.

So, you are right that this limits its use, as seen in the small example above, where values ​​can be assigned at build time, but not when trying to call setter methods.

However, you can increase the usefulness of your code by using wildcards with a lower type , for example:

 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 also starts with the expected results:

 val2: 9, isInt: true val2: 10, isInt: true 

About your second question: I quote a couple directly from your related article:

Using arrays of raw types or unlimited substitution parameters with parameters, we issue checks of a static type that a homogeneous sequence to come with. As a result, we must use explicit casts or risk an unexpected ClassCastException s. In the case of an unlimited parameterized wildcard type, we are further limited by how we can use array elements, since the compiler prevents certain operations with an unlimited parametric wildcard. In essence, arrays of raw types and unlimited substitution parameters are semantically very different from what we will express using an array of a specific substitution parameter. For this reason, they are not a good solution and acceptable only when the superior performance of arrays (compared to collections) is paramount.

The author emphasizes that unlimited wildcards in arrays are not a good workaround due to its limitations and excellent efficiency only in the context of arrays vs collections .

+3
source

An unlimited wildcard is basically syntactic sugar for the case where you need a flexible generic type (for example, when accepting Collection ), but don't really care what the type is (you just print it toString ). You can do the same with the universal method, but it becomes awkward and buys nothing. An unlimited wildcard allows you to discard this.

Explaining arrays simply reminds you that in most cases it is better to use a collection type than an array if you really cannot afford the extra cost of the collection. In your example, you found out why: When you pass an array as Object[] , you tell the compiler that it is perfectly normal to add an object to this array, and since Pair<Integer,Integer> is Object , the compiler allows you to do this. Depending on your other code (you have not yet raised any exception), you probably now have heap pollution in intPairArr .

0
source

Source: https://habr.com/ru/post/952004/


All Articles