The Infer Type Refactoring, Version 3

Purpose

to replace the declared types of declaration elements with ones that have no unneeded members; to minimize coupling between classes

System requirements

Infer Type can be automatically applied by the Declared Type Generalization Checker, which you might want to try as well.

Decription

Infer Type 3 is the latest version of a new Eclipse refactoring that decouples classes by computing for a selected declaration element its maximally general type (interface or abstract class), i.e., that type that contains only the members accessed through the declaration element and the ones it gets assigned to. It then offers the redeclaration of the involved declaration elements with the inferred type (or types, in case this should be necessary).

For users of Eclipse’s other type refactorings, Infer Type can be described as an automatic version of Extract Interface, combined with Generalize Declared Type and Use Supertype Where Possible. However, Infer Type cannot be replaced by any combination of these.

What’s new

Infer Type 3 differs from its predecessor (which is part of the Yoxos Eclipse distribution) in the following respects:

Usage

Examples

Decoupling clients from servers

A client class referencing a server class is coupled to that server class through the reference. If the server offers more than the client needs, this couplig is unnecessarily strong in that the server cannot be replaced by others not offering the unneeded members. For instance, in the code

public class Client {
      Server s;
     
public Client(Server s) {
            s.m();
            this.s = s;
      }
     
void n() {
            s.n();
      }
}

class Client references class Server twice, through its field s and through the formal parameter of its constructor. Depending on what else the server offers in his public interface, this coupling can be reduced by replacing references to Server with an inferred type. Executing Infer Type on field s produces

public interface IServer {
    void n();

}

public
class Server implements IServer {...}

public class Client {
      IServer
s;
      public Client(Server s) {

            s.m();
            this.s = s;
      }
      void n() {

            s.n();
      }
}


executing it on the parameter of the constructor produces

public interface IServer {
    void n();

    void m();
}

public
class Server implements IServer {...}

public class Client {
      IServer
s;
      public Client(IServer s) {

            s.m();
            this.s = s;
      }
      void n() {

            s.n();
      }
}


To make sure that all references of a class to another are replaced with a single new type in a single refactoring, Infer Type must be executed on the corresponding import statement.

Computing the required interface of a class

Given the code

package exp;

public class D {
      public void m() {}
      public void n() {}
      public void o() {}
}

package imp;

import exp.D;

public class C {
      D d;
      D e;
      void m() {d.m(); e.n();}
}

executing Infer Type on the import of D in C will compute and insert a new supertype of D containing only m() and n(). Likewise, in the example of Client and Server above, executing Infer Type on the import of Server in Client will replace all occurrences of Server in Client with one new inferred type, independently of any particular declaration element.

Generalizing type arguments

The Infer Generic Type Arguments refactoring of Eclipse can derive the most specific type parameter for a declaration using a raw type. Conversely, Infer Type can compute the maximally general type parameter for that declaration. For instance, given the code

public class C {
      List<C> l = new ArrayList<C>();
      public void m() {
            C c = l.get(1);
            c.m();
      }
     
public void n() {}
}

executing Infer Type on the type argument C computes a new abstract supertype for C containing only m(), and replaces the type parameter C with it:

public interface I {
    void m();
}
public class C implements I {
      List<I> l = new ArrayList<I>();
      public void m() {
            I c = l.get(1);
            c.m();
      }
     
public void n() {}
}

Things to note

Testing

Infer Type has been automatically applied to all declaration elements of several large code bases.

Known bugs

Installation

Team


The intoJ Team, 2007.