Skip to main content
Jump to: navigation, search

Generify A Java Project

Revision as of 13:48, 6 September 2011 by Markus (Talk | contribs) (New page: This page gives an overview of how to proceed when you want to generify a Java project (use Java 5 generics). It describes the steps we took to generify the org.eclipse.jdt.ui project. Mos...)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

This page gives an overview of how to proceed when you want to generify a Java project (use Java 5 generics). It describes the steps we took to generify the org.eclipse.jdt.ui project. Most of these steps should also be applicable for other projects.



  • Run with enough memory (e.g. -Xmx600M)
  • Make sure you're in sync with HEAD and that other committers are informed about your plans (you don't want to merge non-trivial changes, since you're going to touch a big percentage of your LOC).
  • In MANIFEST.MF, set Bundle-RequiredExecutionEnvironment: J2SE-1.5
  • On the build path, change the "JRE System Library" to "Execution Environment: J2SE-1.5"
  • Make a pass over "Project Properties > Java Compiler > Errors/Warnings" and turn interesting problems for 1.5 language constructs to Warning or Error:
    • Our general rule (JDT UI team) is that we set options to Error iff:
      • there are no false positives
      • we don't have hundreds of instances of the problem and fixing all of them would take too much time
      • it's not a local problem that often occurs during editing and where an error would be distracting
    • Before we commit a file, we always try to get rid of all warnings (if possible with reasonable effort)
  • Adjust code formatter settings for generics and annotations if necessary

Clean Up

  • Run Clean Ups:
    • Missing Code > Add missing Annotations > @Override (but not @Deprecated, which is only redundant)
    • Unnecessary Code > Remove unnecessary casts (you can also enable the others in Unnecessary Code)

Required Projects

  • Generify APIs of required projects. If you have non-generified dependencies, the "Infer Generic Type Arguments" refactoring will wrongly infer <Object> in too many cases (since that's the best guess when APIs use raw types).

E.g to generify the org.eclipse.jdt.core.dom APIs, I used this regex search on the whole project:

\(element ?[Tt]ype: \{@link (\w+)\}\)[^/]*\R\t \*/\R\tpublic List

... and then I just replaced the match with:


This worked quite well because the Javadocs of these APIs are very clean, consistent, and they contain the element type information.

Caveat: After this, client code that calls these APIs and then adds elements to the parameterized list must make sure the elements are of the right type. In some cases, this means that you have to add type parameters to other methods as well.

E.g. in ASTRewrite, the right generic version of:

ASTNode createCopyTarget(ASTNode node)


<T extends ASTNode> T createCopyTarget(T node)

If you can't release these changes, you'll run into trouble when compiling certain client code, e.g.:

	SingleVariableDeclaration p1= xxx;
	SingleVariableDeclaration newParam = (SingleVariableDeclaration) rewrite.createCopyTarget(p1);

The original version of createCopyTarget requires a cast on the second line, but the generified version doesn't. I don't have a good solution for this problem.

Here's another regex I used to generify property descriptors in the DOM AST:

Search: (public static final Child(?:List)?PropertyDescriptor)( [A-Z_]+\s*=\s*new Child(?:List)?PropertyDescriptor)(\(\w+\.class, "\w+", (\w+)\.class)
Replace: \1<\4>\2<\4>\3

Generify your project

  • Manually add type arguments to your subtypes of generic types:
    • Search for implementations of Comparable, Comparator, Iterator, and of the Collection types (Collection, List, Map)
  • Generify your own container types (e.g. MultiMaps, adapters for UI list or tree widgets).
  • Run "Refactor > Infer Generic Type Arguments" on the whole project (both options checked)
  • Organize Imports
  • Fix remaining compile errors manually.
  • Check that the changes make sense and don't affect APIs:
    • Be aware that the Infer Generic Type Arguments refactoring does not create wildcard type bounds, but in APIs, that would often be the right thing to do.
  • Note that some constructs cannot be automatically generified, e.g. custom map implementations, or code that uses the same slots for E and Collection<E>.
  • Search for commented type arguments that have sometimes been used (e.g. Map/*<String, Integer>*/ fStringToInteger;):
    • If equal to the actual arguments, remove the comments:
Search: \s*/\*\s*(<[^>]+>)\s*\*/\s*\1
Replace: \1
(Caveat: Search expression does not work for nested types like List<List<String>>)
    • If different from the actual arguments, take a closer look:
Search:  \s*/\*\s*(<[^>]+>)\s*\*/\s*(?!\1)<[^>]+>
Search:  \w+\s*/\*\s*[^/]+)\s*\*/\s*<[^>]+>
    • Query for Collection/*String*/<String> (note the missing < > in the comment):
Search: \s*/\*\s*<?\s*([^*]+)\s*>?\s*\*/\s*<\1>
Replace: <\1>
  • Make a last manual pass over types with Object as type argument (could be correct, but is often not):
Search: <Object|Object>
  • Check all APIs to make sure their erasure didn't change and the type arguments have the necessary wildcards (roughly "<? super X>" for containers on which you need to call setters, "<? extends X>" for containers on which you need to call getters, and "<X>" for cases that should be completely restricted). Note that adding type parameters and arguments to APIs is like adding new API: You have exactly one chance to get it right. Changing generics later will be a breaking change most of the time.
  • Run "Clean Up > Enhanced for loop" if you want

Closing remarks

  • Set the Problems view to "Group By > Java Problem Type" and try to get get the "Type Safety and Raw Types" group empty.
  • Collection#toArray(T[]) is not typesafe, see Bug 7023484. Be careful when writing new code, since the compiler will not detect bugs in this case.

Copyright © Eclipse Foundation, Inc. All Rights Reserved.