[Java] Eclipse refactoring summary

14 minute read

Eclipse has various refactoring functions, but honestly, many of them do not know what happens when they are executed, so I summarized them.

What is refactoring?

Refactoring refers to the work of improving the internal structure of software while preserving its external behavior.

Reason for refactoring

Improve software design

There is no perfect design. Strictly speaking, even if it seems perfect at the time of design, it will not become perfect over time. Furthermore, no one knows what requests will come out in the future, and who will add the function to where.

An example where the design is not perfect

  • Although I was thinking of only cash and credit card as payment methods, it became necessary to support other cashless payments
  • Due to the short delivery time, we took an ad hoc approach to limit the scope of impact
  • Newly added people add code without understanding existing code and become spaghetti code

The design deteriorates over time, so it is necessary to perform regular maintenance.

Make software easier to understand

The neatly organized code is highly readable and helps developers reduce the time it takes to read the code. It also makes it easier to find bugs.

Development speed improves in the long run

A poor design will accumulate technical debt, apply the same changes to multiple places, do not know the extent of the impact, and will take time to investigate. By continuously refactoring, debt is eliminated, and the number of man-hours required for development is small even if the number of functions increases.

When to avoid refactoring

Unfortunately refactoring is not a silver bullet.

Keep your code salted

If you do not make any changes to your existing code, refactoring has little effect and leaves only the risk. There is no economic rationality there, it is just self-satisfaction.

Externally exposed interface

If you are developing an API and do not know who is using it, you cannot change it.

I can’t persuade my boss

By its very nature, refactoring does not increase system functionality. If your boss doesn’t understand the importance of refactoring, try to persuade yourself. If you cannot convince yourself, give it up without any responsibility. ~~ (And behind the scenes, let’s refactor silently as a software professional.) ~~ It may be avoided when delivery is tight or when you have to manually test all changes.

Refactoring in Eclipse

Refactoring can range from minor changes like renaming local variables to major changes like replacing switch statements with class polymorphism. If the code you are refactoring is protected by tests, you can refactor with confidence, but legacy code doesn’t have tests, so you can’t refactor with confidence. However, as far as the refactoring tools provided in the integrated development environment such as Eclipse have been executed (tested) tens of thousands of times in the world, it is possible to refactor relatively safely even in an environment without automatic tests. Yes. ⚠️ Not 100% safe.

Confirmation environment

Version: 2019-06 (4.12.0) Build id: 20190614-1200

Refactoring method

Select the part you want to refactor and right-click → You can execute it from the refactoring menu. In case of shortcut, it is Alt+Ctrl+T. (Alt++T on Mac)

Notes on executing refactoring

Back up frequently

Refactoring may affect multiple classes and may not be returned by ctrl + z after several runs. Make frequent backups so you can revert your changes.

Run frequently if you have automated tests

Refactoring an IDE is basically safe, but let’s run automated tests as often as possible to make sure that the refactoring doesn’t change the behavior.

If reflection is used, thoroughly investigate the extent of impact

Although a powerful mechanism called reflection is introduced in Java, it cannot benefit from the type checking of the compiler. Even if it is used in the code, it cannot detect that the code has been broken **until it is executed, since it will not be included in the refactoring tool of the IDE. If you are using it in the program, please thoroughly investigate the extent of the impact. If you are developing a framework or plugin, you may be using reflection. ~~ Are you using it to destroy access modifiers in your test code? Who did that? ~~

Each refactoring

Finally, the main subject. I arranged them in order of the ones I often use and the ones I could use, based on my dogma and prejudice.

Rename: star::star::star::star::star:

Applicable target Package, class, method, variable efficacy Change the name. You can change variable names, method names, and class names such as a or instanse2 or abandoned naming, such as a or instanse2, into meaningful names. Optional (if applied to method) Keep original method as delegate to renamed method + mark as deprecated It is also possible to change the externally exposed method etc. while keeping the original method in order to maintain compatibility. When mark is selected as deprecated, @deprecated is added.

before


String getCpyNm() {
  return cpyNm;
}

after


String getCompanyName() {
  return companyName;
}

after

+ option
/**
* Substitute for @deprecated {@link #getCompanyName()}
*/
public String getCpyNm() {
  return getCompanyName();
}

public String getCompanyName() {
  return companyName;
}

Optional (if applied to class)

The following options are available.

  • Update similarly named variables and methods
  • Update text occurrences in comments and strings
  • Fully qualified name updates in non-Java text files

Since it is not safe to change the comment or similar method name instead of referring to it, it is necessary to confirm it in the preview.

Method extraction: star::star::star::star::star:

Applicable target Selected row in method efficacy Separate the selected part as another method. If there are duplicates in the code, multiple duplicates can be replaced together.

before


public void printAverage(int a, int b, int c) {
  int sum = a + b+c;
  int average = sum/3;
  System.out.println(average);
}

after


public void printAverage(int a, int b, int c) {
  int average = getAverage(a, b, c);
  System.out.println(average);
}

private int getAverage(int a, int b, int c) {
  int sum = a + b+c;
  int average = sum/3;
  return average;
}

Extract local variable: star::star::star::star::star:

Applicable target Calculation formula or conditional formula selected in the method efficacy Split long expressions or introduce explanatory variables Reverse of inlining

before


  public int getTotal(int price) {
    int result = (int) (price * 0.8 * 1.10);
    return result;
  }

after


  public int getTotal(int price) {
    double discountPrice = price * 0.8;
    int result = (int) (discountPrice * 1.10);
    return result;
  }

Inlining: star::star::star::star::star:

Applicable target variable efficacy Replace a redundant variable declaration with its value Reverse of local variable extraction

before


  public int getTotal(int price) {
    double discountPrice = price * 0.8;
    int result = (int) (discountPrice * 1.10);
    return result;
  }

after


  public int getTotal(int price) {
    int result = (int) ((price * 0.8) * 1.10);
    return result;
  }

Extracting constants: star::star::star::star:

Applicable target constant efficacy Change the constant to a member variable. Only static values can be used.

before


  public int getTotal(int price) {
    int result = (int) (price * 0.8 * 1.10);
    return result;
  }

after


  public static final double TAX_RATE = 1.10;

  public int getTotal(int price) {
    int result = (int) (price * 0.8 * TAX_RATE);
    return result;
  }

Field encapsulation: star::star::star::star:

Applicable target Member variable efficacy Generate getter/setter of member variable and replace the reference method with the one using getter/setter. option If you set Field access in declarative type to Retain field reference, you can directly refer to the value of the field.

before


public class Refactoring {

  private String companyName = "";private int companyId = 0;

  public Refactoring(String companyName, int companyId) {
    this.companyName = companyName;
    this.companyId = companyId;
  }
}

after


public class Refactoring {

  private String companyName = "";
  private int companyId = 0;

  public Refactoring(String companyName, int companyId) {
    this.companyName = companyName;
    this.setCompanyId(companyId);
  }

  /**
   * @return companyId
   */
  private int getCompanyId() {
    return companyId;
  }

  /**
   * @param companyId companyId to set
   */
  private void setCompanyId(int companyId) {
    this.companyId = companyId;
  }

}

Convert local variable to field: star::star::star::star:

Applicable target Local variable efficacy Change local variables to member variables. Similar to constant extraction option Field access modifier and initialization position can be changed

before


public class Refactoring {
  public int getTotal(int price) {
    double discountRate = 0.8;
    int result = (int) (price * discountRate * 1.10);
    return result;
  }
}

after


public class Refactoring {
  private double discountRate = 0.8;

  public int getTotal(int price) {
    int result = (int) (price * discountRate * 1.10);
    return result;
  }
}

Move: star::star::star:

Applicable target Package, class efficacy Move a class to another package, move/rename a package. If you move a package, you can also change the subpackages.

package com.example.refactoring;
package com.example.refactoring.util;

Applicable target static variable, static method efficacy You can move a static variable or static method to another class. It can also be used for member variables, but since it cannot hold a reference, there is a high probability that a compilation error will occur. . . (Is it refactoring?)

before


public class Refactoring {
  public static final String staticString = "s";

  public static String getStaticString() {
    return staticString;
  }
}

public class OtherClass {
}

after


public class Refactoring {
  public static String getStaticString() {
    return OtherClass.staticString;
  }
}

public class OtherClass {
  public static final String staticString = "s";
}

Method signature change: star::star::star:

Applicable target The method efficacy Add, delete, or change method arguments Troublesome sorting is also possible with copy and paste option If you check Keep original method as delegation to changed method, you can keep the original signature method.

before


  public Refactoring(String companyName) {
    this.companyName = companyName;
  }

after


  public Refactoring(String companyName, String newParam) {
    this.companyName = companyName;
  }

after

 (keep the original method as a delegate to the modified method)
  /**
   * Substitute for @deprecated {@link #Refactoring(String,String)}
   */
  public Refactoring(String companyName) {
    this(companyName, null);
  }

  public Refactoring(String companyName, String newParam) {
    this.companyName = companyName;
  }

Interface extraction: star::star::star:

Applicable target class efficacy Create an interface from existing class. An interface can be created by selecting any public method of an existing class, the created interface is automatically specified as implements, and the method is annotated with @Override.

before


public class Refactoring {
  private String companyName = "";

  public String getCompanyName() {
    return companyName;
  }
}

after


public interface RefactoringInterface {
  String getCompanyName();
}

public class Refactoring implements RefactoringInterface {
  private String companyName = "";

  @Override
  public String getCompanyName() {
    return companyName;
  }
}

Class extraction: star::star::star:

Applicable target Class (actual member variable) efficacy Group member variables into different classes. It is used when the class is enlarged or when related items such as IP address and port number are put together. The method cannot be extracted from the member variables only. option You can select the extraction destination from top-level class or anonymous class. If it is a top-level class, it will be extracted as another class, In the case of anonymous class, the classes are extracted within the same class.

before


public class Refactoring {

  private String companyName = "";
  private String postNo = "";

  public void main() {}
}

after

 (for top-level class)
public class Refactoring {

  private RefactoringData data = new RefactoringData("", "");

  public void main() {}
}

public class RefactoringData {
  public String companyName;
  public String postNo;

  public RefactoringData(String companyName, String postNo) {
    this.companyName = companyName;
    this.postNo = postNo;
  }
}

after

 (for anonymous classes)
public class Refactoring {

  public static class RefactoringData {
    public String companyName;
    public String postNo;

    public RefactoringData(String companyName, String postNo) {
      this.companyName = companyName;
      this.postNo = postNo;
    }
  }

  private RefactoringData data = new RefactoringData("", "");

  public void main() {}
}

Introduction of parameter object :star::star::star:

Applicable target The method efficacy Combine arbitrary arguments as one object option If you check Keep original method as delegation to changed method, you can keep the original signature method. The parameter object to be generated can be selected from either top level class or anonymous class.

before


public class Refactoring {
  public Refactoring(String companyName, int companyId) {
    this.companyName = companyName;
    this.companyId = companyId;
  }
}

after

 (specify the top level class)
public class Refactoring {
 public Refactoring(RefactoringParameter parameterObject) {
    this.companyName = parameterObject.companyName;
    this.companyId = parameterObject.companyId;
  }
}

public class RefactoringParameter {public String companyName;
  public int companyId;

  public RefactoringParameter(String companyName, int companyId) {
    this.companyName = companyName;
    this.companyId = companyId;
  }
}

after

 (specify anonymous class)
public class Refactoring {
  /**
   * Substitute for @deprecated {@link #Refactoring(RefactoringParameter)}
   */
  public Refactoring(String companyName, int companyId) {
    this(new RefactoringParameter(companyName, companyId));
  }

  public Refactoring(RefactoringParameter parameterObject) {
    this.companyName = parameterObject.companyName;
    this.companyId = parameterObject.companyId;
  }
}

public class RefactoringParameter {
  // parameter object is the same
}

Parameter introduction: star::star:

Applicable target Variables and constants in the method efficacy Change variables and constants in the method to method arguments

before


  public String getCompanyName() {
    return "prefix_" + companyName;
  }

after


  public String getCompanyName(String prefix) {
    return prefix + companyName;
  }

Superclass extraction: star::star:

Applicable target class efficacy Create a superclass from an existing class. A superclass can be created by specifying any member variable and method from an existing class, and extends is automatically specified.

before


public class Refactoring {
  private String companyName = "";

  public String getCompanyName() {
    return companyName;
  }
}

after


public class RefactoringSuper {
  private String companyName = "";

  public String getCompanyName() {
    return companyName;
  }
}

public class Refactoring extends RefactoringSuper {
}

Pull up/Push down :star::star:

Applicable target Member variables and methods of superclass/subclass efficacy Move member variables and methods between superclasses/subclasses Pullup moves from subclass to superclass, Pushdown moves from superclass to subclass. Optional (during pushdown) If you select Leave abstract declaration, you can leave the method moved to the superclass as abstruct.

after pullup


public class SuperClass {

  private String companyName = "";

  public String getCompanyName() {
    return companyName;
  }
}

public class Refactoring extends SuperClass {

}

after pushdown


public class SuperClass {

}

public class Refactoring extends SuperClass {
  private String companyName = "";

  public String getCompanyName() {
    return companyName;
  }
}

leave +abstruct declaration after pushdown


public abstract class SuperClass {
  public abstract String getCompanyName();
}

public class Refactoring extends SuperClass {
  private String companyName = "";

  public String getCompanyName() {
    return companyName;
  }
}

Factory introduction: star::star:

Applicable target constructor efficacy Replace the constructor with a static factory method. It is used when you want to make the target class a singleton class or when you want to change the generation method flexibly.

before


public class Refactoring {

  private String companyName = "";

  public Refactoring(String companyName) {
    this.companyName = companyName;
  }
}

after


public class Refactoring {

  public static Refactoring createRefactoring(String companyName) {
    return new Refactoring(companyName);
  }

  private String companyName = "";

  private Refactoring(String companyName) {
    this.companyName = companyName;
  }
}

Use supertype when available: star:

Applicable target Class (effectively referenced in other methods) efficacy If the super class can be used instead, replace it with the super class. In the following example, the Use class declaration has been replaced, but it is SubClass that executes the refactoring.

  • There is no change in the class that executed the refactoring. I think it should be used as a process before deleting the subclass. (Rarely used)

class definition


public class SuperClass {
  protected String companyName = "";

  public String getCompanyName() {
    return companyName;
  }
}
public class Refactoring extends SuperClass {
}

before


public class Use {
  public void main() {
    Refactoring instance = new Refactoring("", 0);
    System.out.println(instance.getCompanyName());
  }
}

after


public class Use {
  public void main() {
    SuperClass instance = new Refactoring("", 0);
    System.out.println(instance.getCompanyName());
  }
}

Move type to new file: star:

Applicable target Anonymous class (inner class) efficacy Move anonymous class to another file

before


public class Refactoring {
  private String companyName = "";
  private int companyId = 0;

  public static class RefactoringParameter {
    public String companyName;
    public int companyId;

    public RefactoringParameter(String companyName, int companyId) {
      this.companyName = companyName;
      this.companyId = companyId;
    }
  }

  public Refactoring(RefactoringParameter parameterObject) {
    this.companyName = parameterObject.companyName;
    this.companyId = parameterObject.companyId;
  }
}

after


public class Refactoring {

  private String companyName = "";
  private int companyId = 0;

  public Refactoring(RefactoringParameter parameterObject) {
    this.companyName = parameterObject.companyName;
    this.companyId = parameterObject.companyId;
  }
}

public class RefactoringParameter {
  public String companyName;
  public int companyId;

  public RefactoringParameter(String companyName, int companyId) {
    this.companyName = companyName;
    this.companyId = companyId;
  }
}

Generalization of declared types: star:

Applicable target Member variable type, method return value type, variable type efficacy Replace the type with the superclass type. You can change it to a superclass like String type → Object type. However, it cannot be changed if it is referenced as a String type in another class and is not compatible.

before

public String getCompanyName() {
   return companyName;
}

after


public Object getCompanyName() {
   return companyName;
}

Finally

There were a lot of functions to learn about Eclipse refactoring for the first time. The number of :star: has been reduced for those I didn’t know how to use it personally. Please let me know if you have any convenient uses. It’s not 100% safe, but it’s overwhelmingly faster and safer to refactor than to fix it by hand after making changes, so let’s refactor it steadily.