Restrictions and Limitations

   


In the following sections, we discuss a number of restrictions that you need to consider when working with Java generics. Most of these restrictions are a consequence of type erasure.

Primitive Types

You cannot substitute a primitive type for a type parameter. Thus, there is no Pair<double>, only Pair<Double>. The reason is, of course, type erasure. After erasure, the Pair class has fields of type Object, and you can't use them to store double values.

This is an annoyance, to be sure, but it is consistent with the separate status of primitive types in the Java language. It is not a fatal flaw there are only eight primitive types, and you can always handle them with separate classes and methods when wrapper types are not an acceptable substitute.

Runtime Type Inquiry

Objects in the virtual machine always have a specific nongeneric type. Therefore, all type inquiries yield only the raw type. For example,

 if (a instanceof Pair<String>) // same as a instanceof Pair 

really only tests whether a is a Pair of any type. The same is true for the test

 if (a instanceof Pair<T>) // T is ignored 

or the cast

 Pair<String> p = (Pair<String>) a; // WARNING--can only test that a is a Pair 

To remind you of the risk, you will get a compiler warning whenever you use instanceof or cast expressions that involve generic types.

In the same spirit, the getClass method always returns the raw type. For example,

 Pair<String> stringPair = . . .; Pair<Employee> employeePair = . . .; if (stringPair.getClass() == employeePair.getClass()) // they are equal 

The comparison yields true because both calls to getClass return Pair.class.

Exceptions

You can neither throw nor catch objects of a generic class. In fact, it is not even legal for a generic class to extend Throwable. For example, the following definition will not compile:

 public class Problem<T> extends Exception { /* . . . */ } // ERROR--can't extend Throwable 

You cannot use a type variable in a catch clause. For example, the following method will not compile:


public static <T extends Throwable> void doWork(Class<T> t)
{
   TRy
   {
      do work
   }
   catch (T e) // ERROR--can't catch type variable
   {
      Logger.global.info(...)
   }
}

However, it is ok to use type variables in exception specifications. The following method is legal:


public static <T extends Throwable> void doWork(T t) throws T // OK
{
   TRy
   {
      do work
   }
   catch (Throwable realCause)
   {
      t.initCause(realCause);
      throw t;
   }
}

Arrays

You cannot declare arrays of parameterized types, such as

 Pair<String>[] table = new Pair<String>(10); // ERROR 

What's wrong with that? After erasure, the type of table is Pair[]. You can convert it to Object[]:

 Object[] objarray = table; 

We discussed in Chapter 5 that an array remembers its component type and throws an ArrayStoreException if you try to store an element of the wrong type:

 objarray[0] = "Hello"; // ERROR--component type is Pair 

But erasure renders this mechanism ineffective for generic types. The assignment

 objarray[0] = new Pair<Employee>(); 

would pass the array store check but still result in a type error. For this reason, arrays of parameterized types are outlawed.

TIP

If you need to collect parameterized type objects, simply use an ArrayList: ArrayList<Pair<String>> is safe and effective.


Instantiation of Generic Types

You cannot instantiate generic types. For example, the following Pair<T> constructor is illegal:

 public Pair() { first = new T(); second = new T(); } // ERROR 

Type erasure would change T to Object, and surely you don't want to call new Object().

Similarly, you cannot make a generic array:

 public <T> T[] minMax(T[] a) { T[] mm = new T[2]; . . . } // ERROR 

Type erasure would cause this method to always construct an array Object[2].

However, you can construct generic objects and arrays through reflection, by calling the Class.newInstance and Array.newInstance methods.

Static Contexts

You cannot reference type variables in static fields or methods. For example, the following clever idea won't work:


public class Singleton<T>
{
   public static T getSingleInstance() // ERROR
   {
      if (singleInstance == null) construct new instance of T
      return singleInstance;
   }
   private static T singleInstance; // ERROR
}

If this could be done, then a program could declare a Singleton<Random> to share a random number generator and a Singleton<JFileChooser> to share a file chooser dialog. But it can't work. After type erasure there is only one Singleton class, and only one singleInstance field. For that reason, static fields and methods with type variables are simply outlawed.

Clashes after Erasure

It is illegal to create conditions that cause clashes when generic types are erased. Here is an example. Suppose we add an equals method to the Pair class, like this:

 public class Pair<T> {    public boolean equals(T value) { return first.equals(value) && second.equals(value); }    . . . } 

Consider a Pair<String>. Conceptually, it has two equals methods:

 boolean equals(String) // defined in Pair<T> boolean equals(Object) // inherited from Object 

But the intuition leads us astray. The erasure of the method

 boolean equals(T) 

is

 boolean equals(Object) 

which clashes with the Object.equals method.

The remedy is, of course, to rename the offending method.

The generics specification cites another rule: "To support translation by erasure, we impose the restriction that a class or type variable may not at the same time be a subtype of two interface types which are different parameterizations of the same interface." For example, the following is illegal:

class Calendar implements Comparable<Calendar> { . . . } class GregorianCalendar extends Calendar implements Comparable<GregorianCalendar> { . . . } // ERROR

GregorianCalendar would then implement both Comparable<Calendar> and Comparable<GregorianCalendar>, which are different parameterizations of the same interface.

It is not clear what this restriction has to do with type erasure. The nongeneric version

 class Employee implements Comparable { . . . } class Manager extends Employee implements Comparable { . . . } 

is legal.


       
    top



    Core Java 2 Volume I - Fundamentals
    Core Java(TM) 2, Volume I--Fundamentals (7th Edition) (Core Series) (Core Series)
    ISBN: 0131482025
    EAN: 2147483647
    Year: 2003
    Pages: 132

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