Features Releases Tutorials

Refactoring: Extract Variable in IntelliJ IDEA

As a code base ages, new code is added, and the existing code is modified to meet the business requirements. This can often have a negative impact on the design and structure of the code. Developers may fear working with such code as it is difficult to understand, modify, or extend.

As a developer, you can take redundancy and complexity out of your code, by refactoring it. Refactoring is the process of improving your source code without creating any new functionality. It helps you keep your code SOLID, DRY, and easy to maintain. Even though clean code bases and code refactoring is crucial (code maintenance accounts for about 80% of a project’s cost), it is often avoided or put off because it can break your existing code.

In this blog post, we will take a look at how IntelliJ IDEA can help you to refactor your code safely with Variable refactorings, and talk about some of the dos and don’ts we need to be aware of. Let’s get started.

Extract Variable Refactoring

The Extract Variable refactoring lets you simplify an expression and remove its redundant parts.

If an expression is hard to understand or it is duplicated in several places throughout your code, you can place the result of such expression (or a part of it) into a separate variable that is less complex and easier to understand.

Introducing a variable for readability

Let’s introduce a variable to simplify the expression that assigns a value to the variable result. Select the expression you want to extract into another variable, and press Ctrl+Alt+V (on Windows)/ Cmd+Alt+V (on Mac). You can also choose to define the extracted variable as final or declared using type var. IntelliJ IDEA gives a default name to the extracted variable, which you can then modify:

Reducing duplicate sub-expressions

IntelliJ IDEA is quick to detect if the expression that you are extracting into another variable is duplicated in multiple places. If it is, then it offers to replace one or all the occurrences of the selected expression. You can also change the default variable name suggested by IntelliJ IDEA.

If you are replacing multiple occurrences of an expression, IntelliJ IDEA can detect where and how you have used it. For example, if an expression is used both in the then and else branches of an if statement, IntelliJ IDEA extracts and defines it before it starts:

Part of the expression that you extract into a variable could also include a method call. Let’s have a look at how IntelliJ IDEA can help you replace one or all of these occurrences:

Extract variable from field initializer

(New in IntelliJ IDEA 2019.1)

You can find complex expressions in various places, including a field initializer. When you try to simplify such an expression, IntelliJ IDEA will move the extracted variable to an instance initializer. If you think that that’s unexpected – read on.

If you decompile bytecodes of a class, you’ll realize that a field (or instance variable) is initialized in a class constructor. So even if you initialize a field with its declaration, the compiler would move its initialization code to the constructor. Also, all the code in the instance initializers is moved to class constructors, in the order that they appear in the class. This explains why IntelliJ IDEA moves the extracted variable from a field initializer to an instance initializer, as demonstrated here:

Similarly, if a static variable uses a complex expression in its initialization, IntelliJ IDEA can simplify the expression for you. Just select the part of the expression you want to extract in a new extract variable. With static variables, this code would be defined within a static initializer block:

Simplifying expressions with ternary operator

(New in IntelliJ IDEA 2019.1)

Ternary operators offer a concise way to define a simple ifelse statement. You can evaluate a boolean expression and return a value based on its result. However, a ternary operator can become complex, especially if it includes checks – such as instanceof for a variable.

In such cases, IntelliJ IDEA can help you extract a variable and also replace the ternary operator with an if statement:

Here’s a similar example of a seemingly complex ternary operator that includes a null check. Again, IntelliJ IDEA can help you extract a variable and replace the ternary operator with an ifelse statement:

However, if the condition used in your ternary operator is not complex (with a null check or instanceof operator), it doesn’t make sense to automatically convert it to an ifelse statement. The essence of refactoring is to make your code simple to read and maintain. In cases like this, IntelliJ IDEA applies simple refactoring – keeping your ternary operator, and extracting the selected expression into a variable:

Automatic refactoring is not a magic bullet

To use variable refactorings in IntelliJ IDEA effectively, you must know when it works safely and when you need to exercise caution.

For example, in the following code, extracting emp.title into a variable and replacing all its occurrences could output unexpected results. Calling emp.title on line1 and line2, might return different values:

void test(Employee emp, Runnable runnable) {
    if (emp != null) {
        log("Starting " + emp.title);            // line 1
    }
    runnable.run();                              
    if (emp != null) {
        log("Finishing " + emp.title);           // line 2
    }
}

Similarly, when you extract an expression that includes a method call, IntelliJ IDEA lets you choose whether you want to replace just one occurrence or all of them. Be careful in such cases – such extraction may change your code behavior, irrespective of whether the method call that you extract into a variable is pure or not.

Impure methods can output different results when called with the same parameters. Even for pure functions, the mutable arguments you use to invoke your method might change between its invocations.

Here’s an example of an impure method variableValue, whose return value is dependent on when it is called (since it returns the ‘seconds’ value of the time it executes at):

public class ExtractVarMethodCall {
    public int calcResult(int total) {
        int a = 100;
        int b = a + getVariableValue();
        int c = b + getVariableValue();
        return c;
    }

    private int getVariableValue() {
        return LocalTime.now().getSecond();
    }
}

You could refactor the preceding code, as follows:

final var variableValue = getVariableValue();
int b = a + variableValue;
int c = b + variableValue;

In the preceding refactored code, the method variableValue executes once and the same value is used twice – which wasn’t the intent of the original code. So this code won’t execute as expected.

Automated tests to the rescue

Before you get started with some serious refactoring of your code, please ensure you have an automated test suite set up to ensure all the functionality is still the same after the modifications. Here’s how IntelliJ IDEA can quickly help you to get started with defining unit tests.

Happy refactoring!

image description