Refactoring with type constraints – Frank Tip

Frank Tip presents his long running (2002-now) work on automated refactoring tools for Java, with a focus on class refactorings.

Refactoring for generalization

An example of this is ‘extract interface’, where you start with a concrete implementation and generalize ‘after the fact’. Other examples are ‘extract superclass’, ‘generalize declared type’ or ‘pull up member’ and ‘use supertype where possible’. All are covered in chapter 11 of Martin Fowler’s Refactoring book.

Turned out it was non-trivial to implement the extract interface step, especially updating existing references turned out to be tricky. To determine whether replacement by the new supertype is possible, we have to make sure it is not used in placed where fields are accessed (Java does not allow this), and if there are calls to a method where this happens, replacement there is illegal to.

Frank solved this by extracting a set of type constraints from a program (and the original program is one possible solution) Now, other solutions correspond to refactored versions of the program. Heuristics are used to guide the solver towards more desired solutions.

Example pf constraints generated from a program

Example of constraints generated from a program

Introducing generics

When generics are introduced in a language that did not have generics, first, we have to introduce the type parameter and secondly, we have to infer generic type. Then, casts that have become redundant need to be removed.

Again, type constraints could be generated to express the conditions of a refactoring.

20140519_102940

Based on the constraints, replacements for the types can be generated:

20140519_103347

Frank evaluate this approach and found that on average 52% of casts could be removed. Also, in another experiment, the automated results were compared with the programs that programmers made manually and in 95% of the cases the same programs were created.

Class migration

Another application of refactoring is to replace existing client code in case of API changes, like StringBuffer -> StringBuilder or Vector -> ArrayList. In order to use type constraints, we need to provide the system with a list of migrations of class names and method names, like Vector -> ArrayList and nextElement() -> next().

Then, constraints can be used to infer how to rewrite the program.

Conclusion

Statically typed languages are becoming more and more complex (closures, extension methods), so there will be more opportunities for refactoring to add more flexibility ‘after the fact’. Challenges are that if languages get more powerful, the constraint systems will also get more and more complex.