Section 4.2. Static Members


4.2. Static Members

Because generics are compiled by erasure, at run time the classes List<Integer>, List<String>, and List<List<String>> are all implemented by a single class, namely List. You can see this using reflection:

 List<Integer> ints = Arrays.asList(1,2,3); List<String> strings = Arrays.asList("one","two"); assert ints.getClass() == strings.getClass(); 

Here the class associated with a list of integers at run time is the same as the class associated with a list of strings.

One consequence is that static members of a generic class are shared across all instantiations of that class, including instantiations at different types. Static members of a class cannot refer to the type parameter of a generic class, and when accessing a static member the class name should not be parameterized.

For example, here is a class, Cell<T>, in which each cell has an integer identifier and a value of type T:

 class Cell<T> {   private final int id;   private final T value;   private static int count = 0;   private static synchronized int nextId() { return count++; }   public Cell(T value) { this.value=value; id=nextId(); }   public T getValue() { return value; }   public int getId() { return id; }   public static int getCount() { return count; } } 

A static field, count, is used to allocate a distinct identifier to each cell. The static nextId method is synchronized to ensure that unique identifiers are generated even in the presence of multiple threads. The static getCount method returns the current count.

Here is code that allocates a cell containing a string and a cell containing an integer, which are allocated the identifiers 0 and 1, respectively:

 Cell<String> a = new Cell<String>("one"); Cell<Integer> b = new Cell<Integer>(2); assert a.getId() == 0 && b.getId() == 1 && Cell.getCount() == 2; 

Static members are shared across all instantiations of a class, so the same count is incremented when allocating either a string or an integer cell.

Because static members are independent of any type parameters, we are not permitted to follow the class name with type parameters when accessing a static member:

 Cell.getCount();           // ok Cell<Integer>.getCount();  // compile-time error Cell<?>.getCount();        // compile-time error 

The count is static, so it is a property of the class as a whole, not any particular instance.

For the same reason, you may not refer to a type parameter anywhere within a static member. Here is a second version of Cell, which attempts to use a static variable to keep a list of all values stored in any cell:

 class Cell2<T> {   private final T value;   private static List<T> values = new ArrayList<T>(); // illegal   public Cell(T value) { this.value=value; values.add(value); }   public T getValue() { return value; }   public static List<T> getValues() { return values; } // illegal } 

Since the class may be used with different type parameters at different places, it makes no sense to refer to T in the declaration of the static field values or the static method getValues(), and these lines are reported as errors at compile time. If we want a list of all values kept in cells, then we need to use a list of objects, as in the following variant:

 class Cell2<T> {   private final T value;   private static List<Object> values = new ArrayList<Object>();  // ok   public Cell(T value) { this.value=value; values.add(value); }   public T getValue() { return value; }   public static List<Object> getValues() { return values; }  // ok } 

This code compiles and runs with no difficulty:

 Cell2<String> a = new Cell2<String>("one"); Cell2<Integer> b = new Cell2<Integer>(2); assert Cell2.getValues().toString().equals("[one, 2]"); 




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