Moving JFace to Java 1.5

With the Fix of Bug 395213 we moved the requirements of the org.eclipse.jface bundle J2SE-1.5 which enables the language features of Java 1.5 for the JFace bundle. But the enabling of the language features(e.g. Generics) also brought us the compiler warnings of Java 1.5. So there was a warning for every untyped maps, lists, sets and other generified classes.
Currently I’m trying to fix these warnings and in this post I would like to describe which steps I’m following to get rid of most of the warnings.
My approach is mainly based on the wiki article Generify A Java Project which is well worth a reading.
First of all we decided to split the fixes up into the packages of the JFace bundle, so every package like org.eclipse.jface.action gets it’s own bug on bugzilla and a it’s own gerrit review. The reason for this decision was to keep the loc of the reviews small.

For each of these packages I intend to follow these steps:

  1. Manually go through the coding and add as most types as possible
  2. Run “Infer Generic Type Arguments” which tries to add the correct types by identifying the types that were used with this class.
  3. Check the result after inferring the types. At some points <Object> will be inferred as type. This type is mostly wrong and you should be careful with this type.
  4. Fix remaining warnings and errors manually

The last step is the hardest, because the work with generics has some traps which you should be aware of. Here you see some of the problems which could occur:

Arrays and generics do not mix very well
In some cases you will find array creations of the type Object like in the following coding.

private Object[] elements;
//...
public LabelProviderChangedEvent(IBaseLabelProvider source, Object
element) {
super(source);
this.elements = new Object[1];
this.elements[0] = element;
}

But if you are trying to replace Object with an erasure, the instantiation is not possible. The best way would be using a typed lists. But this may break the compatibility and result in performance issues. According to this problem, the best way the best way to go would be:

private E[] elements;
//...
public LabelProviderChangedEvent(IBaseLabelProvider source, E
element) {
super(source);
@SuppressWarnings("unchecked")
this.elements = (E[]) new Object[1]; //Warning
this.elements[0] = element;
}

Generics are invariant or List<Object> != List<String>
As mention before, the “Infer Generic Type Arguments” command often infers the type <Object> which is often not correct.

For example, the following code is actually working. Of course String is a sub-type of Object.

String[] aString = { "Bla"};
Object[] aObject;
aObject = aString;

If you translate this into generics your code would probably look like this.

List<String> lString;
List<Object> lObject ;
lObject = lString;

But this won’t work, because generics are invariant or easily said List<String> is not a sub-type of List<Object>

A solution for this would be the wildcard parameter <?> like in the following example.

List<String> lString = new ArrayList<String>();
List<?> lObject ;
lObject = lString;

With wildcards it is also possible to define a upper bound (<? extends Person>) or a lower bound (<? super Person>) which allows you to restrict the used types.

So it is very important to know which type is used, when it comes to changes of signatures, which are available to clients.

Links:
http://wiki.eclipse.org/Generify_A_Java_Project
http://eclipse.org/eclipse/presentation/eclipsecon/API-Tutorial-EclipseCon-2007.pdf
Book: Effective Java 2nd Edition
http://docs.oracle.com/javase/tutorial/java/generics/wildcards.html

This entry was posted in Eclipse, Hendrik Still, Other and tagged . Bookmark the permalink.