Section 6.6. The Principle of Indecent Exposure


6.6. The Principle of Indecent Exposure

Although it is an error to create an array with a component type that is not reifiable, it is possible to declare an array with such a type and to perform an unchecked cast to such a type. These features must be used with extreme caution, and it is worthwhile to understand what can go wrong if they are not used properly. In particular, a library should never publicly expose an array with a nonreifiable type.

Recall that Section 2.5 presents an example of why reification is necessary:

 Integer[] ints = new Integer[] {1}; Number[] nums = ints; nums[0] = 1.01;  // array store     exception     int n = ints[0]; 

This assigns an array of integers to an array of numbers, and then attempts to store a double into the array of numbers. The attempt raises an array store exception because of the check against the reified type. This is just as well, since otherwise the last line would attempt to store a double into an integer variable.

Here is a similar example, where arrays of numbers are replaced by arrays of lists of numbers:

 List<Integer>[] intLists   = (List<Integer>[])new List[] {Arrays.asList(1)}; // unchecked cast List<? extends Number>[] numLists = intLists; numLists[0] = Arrays.asList(1.01); int n = intLists[0].get(0);  // class cast exception! 

This assigns an array of lists of integers to an array of lists of numbers, and then attempts to store a list of doubles into the array of lists of numbers. This time the attempted store does not fail, even though it should, because the check against the reified type is inadequate: the reified information contains only the erasure of the type, indicating that it is an array of List, not an array of List<Integer>. Hence the store succeeds, and the program fails unexpectedly elsewhere.

Example 6.1 presents a similar example, divided into two classes in order to demonstrate how a poorly designed library can create problems for an innocent client. The first class, called DeceptiveLibrary, defines a static method that returns an array of lists of integers of a given size. Since generic array creation is not permitted, the array is created with components of the raw type List, and a cast is used to give the components the parameterized type List<Integer>. The cast generates an unchecked warning:

Example 6-1. Avoid arrays of nonreifiable type

 DeceptiveLibrary.java: import java.util.*; public class DeceptiveLibrary {   public static List<Integer>[] intLists(int size) {     List<Integer>[] intLists =       (List<Integer>[]) new List[size];  // unchecked cast     for (int i = 0; i < size; i++)       intLists[i] = Arrays.asList(i+1);     return ints;   } } InnocentClient.java: import java.util.*; public class InnocentClient {   public static void main(String[] args) {     List<Integer>[] intLists = DeceptiveLibrary.intLists(1);     List<? extends Number>[] numLists = intLists;     numLists[0] = Arrays.asList(1.01);     int i = intLists[0].get(0);  // class cast error!   } } 

 %javac -Xlint:unchecked DeceptiveLibrary.java DeceptiveLibrary.java:5: warning: [unchecked] unchecked cast found   : java.util.List[] required: java.util.List<java.lang.Integer>[]       (List<Integer>[]) new List[size];  // unchecked cast                         ^ 1 warning 

Since the array really is an array of lists of integers, the cast appears reasonable, and you might think that this warning could be safely ignored. As we shall see, you ignore this warning at your peril!

The second class, called InnocentClient, has a main method similar to the previous example. Because the unchecked cast appears in the library, no unchecked warning is issued when compiling this code. However, running the code overwrites a list of integers with a list of doubles. Attempting to extract an integer from the array of lists of integers causes the cast implicitly inserted by erasure to fail:

 %java InnocentClient Exception in thread "main" java.lang.ClassCastException: java.lang.Double         at InnocentClient.main(InnocentClient.java:7) 

As in the previous section, this error message may be confusing, since that line does not appear to contain a cast!

In order to avoid this problem, you must stick to the following principle:

Principle of Indecent Exposure: never publicly expose an array where the components do not have a reifiable type.

Again, this is a case where an unchecked cast in one part of the program may lead to a class cast error in a completely different part, where the cast does not appear in the source code but is instead introduced by erasure. Since such errors can be extremely confusing, unchecked casts must be used with extreme caution.

The Principle of Truth in Advertising and the Principle of Indecent Exposure are closely linked. The first requires that the run-time type of an array is properly reified, and the second requires that the compile-time type of an array must be reifiable.

It has taken some time for the importance of the Principle of Indecent Exposure to be understood, even among the designers of generics for Java. For example, the following two methods in the reflection library violate the principle:

 TypeVariable<Class<T>>[] java.lang.Class.getTypeParameters() TypeVariable<Method>[] java.lang.Reflect.Method.getTypeParameters() 

Following the preceding model, it is not hard to create your own version of InnocentClient that throws a class cast error at a point where there is no cast, where in this case the role of DeceptiveLibrary is played by the official Java library! (At the time of going to press, remedies for this bug are under consideration. Possible fixes are to delete the type parameter from TypeVariable so that the methods return an array of reified type, or to replace the arrays with lists.)

Don't get caught out in the same waybe sure to follow the Principle of Indecent Exposure rigorously in your own code!




Java Generics and Collections
Java Generics and Collections
ISBN: 0596527756
EAN: 2147483647
Year: 2006
Pages: 136

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net