Features

Refactoring: Inline Method in IntelliJ IDEA

The idea of Inline Method Refactoring is simple – replace a method call with its contents. Still, it is really powerful. But to appreciate its power, you need to know its use cases.

Let’s understand why, when, and how to apply the Inline Method refactoring and how IntelliJ IDEA can help you get started.

Disruption in the flow of a method

When a method call disrupts the flow in your method rather than simplifying it, you can consider applying Inline Method. In the following code, the call to the method compareRatingWithMethodParameter() is inserting unnecessary indirection in the method scheduleSession().

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
public class Talk {
int rating;
LocalTime scheduleSession() {
return compareRatingWithMethodParameter(3) ?
LocalTime.of(9, 0) :
LocalTime.of(10, 0);
}
private boolean compareRatingWithMethodParameter(int param) {
return rating < param;
}
}
public class Talk { int rating; LocalTime scheduleSession() { return compareRatingWithMethodParameter(3) ? LocalTime.of(9, 0) : LocalTime.of(10, 0); } private boolean compareRatingWithMethodParameter(int param) { return rating < param; } }
public class Talk {
    int rating;

    LocalTime scheduleSession() {
        return compareRatingWithMethodParameter(3) ?
                LocalTime.of(9, 0) :
                LocalTime.of(10, 0);
    }

    private boolean compareRatingWithMethodParameter(int param) {
        return rating < param;
    }
}

The preceding code could be simplified by inlining the method compareRatingWithMethodParameter():

GIF

However, you don’t have to inline every method that defines just one line of code. The decision should depend on whether it is helping you to understand the flow in your method.

Code Migration, or using the latest Java language features

When migrating your code to a later Java version, you may want to inline a couple of methods so that you can apply an operation to code from multiple methods, collectively.
Though the code in the method getSortedListOfNames() looks readable, by inlining the methods extractNamesFromSpeakerList() and sortSpeakerNames(), you can refactor it to use Java streams instead of using the for loop and Collections.sort methods separately. Here’s the original code:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
List<String> getSortedListOfNames(List<Speaker> speakers) {
List<String> speakerNames = extractNamesFromSpeakerList(speakers);
sortSpeakerNames(speakerNames);
return speakerNames;
}
private void sortSpeakerNames(List<String> speakerNames) {
Collections.sort(speakerNames);
}
private List<String> extractNamesFromSpeakerList(List<Speaker> speakers) {
List<String> result = new ArrayList<>();
for (Speaker speaker : speakers) {
result.add(speaker.getName());
}
return result;
}
List<String> getSortedListOfNames(List<Speaker> speakers) { List<String> speakerNames = extractNamesFromSpeakerList(speakers); sortSpeakerNames(speakerNames); return speakerNames; } private void sortSpeakerNames(List<String> speakerNames) { Collections.sort(speakerNames); } private List<String> extractNamesFromSpeakerList(List<Speaker> speakers) { List<String> result = new ArrayList<>(); for (Speaker speaker : speakers) { result.add(speaker.getName()); } return result; }
    List<String> getSortedListOfNames(List<Speaker> speakers) {
        List<String> speakerNames = extractNamesFromSpeakerList(speakers);
        sortSpeakerNames(speakerNames);
        return speakerNames;
    }

    private void sortSpeakerNames(List<String> speakerNames) {
        Collections.sort(speakerNames);
    }

    private List<String> extractNamesFromSpeakerList(List<Speaker> speakers) {
        List<String> result = new ArrayList<>();
        for (Speaker speaker : speakers) {
            result.add(speaker.getName());
        }
        return result;
    }

Here’s how you can inline the methods and refactor the resulting code:

GIF

Here’s the refactored code for you to compare with the original code:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
List<String> getSortedListOfNames(List<Speaker> speakers) {
return speakers.stream()
.map(Speaker::getName)
.sorted()
.collect(Collectors.toList());
}
List<String> getSortedListOfNames(List<Speaker> speakers) { return speakers.stream() .map(Speaker::getName) .sorted() .collect(Collectors.toList()); }
    List<String> getSortedListOfNames(List<Speaker> speakers) {
    return speakers.stream()
            .map(Speaker::getName)
            .sorted()
            .collect(Collectors.toList());
}

Group of badly refactored methods

Often programmers end up applying refactoring practices poorly. For example, in the following code, the programmer seems to have applied ‘Extract Method Refactoring’ to each individual line of code.

Though it still looks readable, inlining a few of these methods and combining the code of one or more methods into another method, followed by (sensible) refactoring, can improve the intent of the code:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
List<Speaker> getPanelists(Track track, List<Speaker> speakerList) {
outputTrackName(track);
outputSpeakerList(speakerList);
List<Speaker> panelists = filterSpeakersWithoutTracks(speakerList);
panelists = findSpeakerSpeakingOnTrack(track, panelists);
return panelists;
}
private void outputTrackName(Track track) {
System.out.println("Looking for panelists for track : " + track);
}
private void outputSpeakerList(List<Speaker> list) {
list.forEach(System.out::println);
}
private List<Speaker> filterSpeakersWithoutTracks(List<Speaker> list) {
return list.stream().
filter(s -> s.speakingOn != null)
.collect(Collectors.toList());
}
private List<Speaker> findSpeakerSpeakingOnTrack(Track track, List<Speaker> list) {
return list.stream()
.filter(s -> s.isSpeakingOn(track))
.collect(Collectors.toList());
}
List<Speaker> getPanelists(Track track, List<Speaker> speakerList) { outputTrackName(track); outputSpeakerList(speakerList); List<Speaker> panelists = filterSpeakersWithoutTracks(speakerList); panelists = findSpeakerSpeakingOnTrack(track, panelists); return panelists; } private void outputTrackName(Track track) { System.out.println("Looking for panelists for track : " + track); } private void outputSpeakerList(List<Speaker> list) { list.forEach(System.out::println); } private List<Speaker> filterSpeakersWithoutTracks(List<Speaker> list) { return list.stream(). filter(s -> s.speakingOn != null) .collect(Collectors.toList()); } private List<Speaker> findSpeakerSpeakingOnTrack(Track track, List<Speaker> list) { return list.stream() .filter(s -> s.isSpeakingOn(track)) .collect(Collectors.toList()); }
    List<Speaker> getPanelists(Track track, List<Speaker> speakerList) {
        outputTrackName(track);
        outputSpeakerList(speakerList);
        List<Speaker> panelists = filterSpeakersWithoutTracks(speakerList);
        panelists = findSpeakerSpeakingOnTrack(track, panelists);
        return panelists;
    }

    private void outputTrackName(Track track) {
        System.out.println("Looking for panelists for track : " + track);
    }

    private void outputSpeakerList(List<Speaker> list) {
        list.forEach(System.out::println);
    }

    private List<Speaker> filterSpeakersWithoutTracks(List<Speaker> list) {
        return list.stream().
                filter(s -> s.speakingOn != null)
                .collect(Collectors.toList());
    }

    private List<Speaker> findSpeakerSpeakingOnTrack(Track track, List<Speaker> list) {
        return list.stream()
                .filter(s -> s.isSpeakingOn(track))
                .collect(Collectors.toList());
    }

Let’s see how we can reorganize the code. Let’s inline a couple of methods and then extract code to methods, to make the code concise and readable.

GIF

Again, here’s the final code for you to compare with the initial code:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
List<Speaker> getPanelists(Track track, List<Speaker> speakerList) {
outputParamValues(track, speakerList);
return speakerList.stream().
filter(s -> s.speakingOn != null && s.isSpeakingOn(track))
.collect(Collectors.toList());
}
private void outputParamValues(Track track, List<Speaker> speakerList) {
System.out.println("Looking for panelists for track : " + track);
speakerList.forEach(System.out::println);
}
List<Speaker> getPanelists(Track track, List<Speaker> speakerList) { outputParamValues(track, speakerList); return speakerList.stream(). filter(s -> s.speakingOn != null && s.isSpeakingOn(track)) .collect(Collectors.toList()); } private void outputParamValues(Track track, List<Speaker> speakerList) { System.out.println("Looking for panelists for track : " + track); speakerList.forEach(System.out::println); }
    List<Speaker> getPanelists(Track track, List<Speaker> speakerList) {
        outputParamValues(track, speakerList);
        return speakerList.stream().
                filter(s -> s.speakingOn != null && s.isSpeakingOn(track))
                .collect(Collectors.toList());
    }

    private void outputParamValues(Track track, List<Speaker> speakerList) {
        System.out.println("Looking for panelists for track : " + track);
        speakerList.forEach(System.out::println);
    }

Inline Method improvements

(New in IntelliJ IDEA 2019.2)

IntelliJ IDEA 2019.2 includes significant improvements to the Inline Method refactoring.
A method might include multiple exit points by defining multiple return statements. When you inline such a method in IntelliJ IDEA 2019.2, it can be modified to define just one exit point in the form of a single return statement, to exit the method. IntelliJ IDEA refers to this as the ‘transforms to single exit point’ feature.

In the following code, when you inline the method isBreakRequiredBetweenSession(), the process introduces a variable result. The multiple return statements are replaced with an assignment to the variable result and just one return statement at the end:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
void outputBreak() {
boolean breakReq = isBreakRequiredBetweenSession();
System.out.println("Break required = " + breakReq);
}
private boolean isBreakRequiredBetweenSession() {
if (isWeekend())
if (isDurationGreaterThan50min())
return true;
else
return false;
else
return false;
}
void outputBreak() { boolean breakReq = isBreakRequiredBetweenSession(); System.out.println("Break required = " + breakReq); } private boolean isBreakRequiredBetweenSession() { if (isWeekend()) if (isDurationGreaterThan50min()) return true; else return false; else return false; }
    void outputBreak() {
        boolean breakReq = isBreakRequiredBetweenSession();
        System.out.println("Break required = " + breakReq);
    }

    private boolean isBreakRequiredBetweenSession() {
        if (isWeekend())
            if (isDurationGreaterThan50min())
                return true;
            else
                return false;
        else
            return false;
    }

Let’s inline the method isBreakRequiredBetweenSession():

GIF

As you can see, the modified code is simpler to follow – since it doesn’t have multiple exit points. Here’s the final code for you to compare with the initial code:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
void outputBreak() {
boolean breakReq;
if (isWeekend())
if (durationGreaterThan50min())
breakReq = true;
else
breakReq = false;
else
breakReq = false;
System.out.println("Break required = " + breakReq);
}
void outputBreak() { boolean breakReq; if (isWeekend()) if (durationGreaterThan50min()) breakReq = true; else breakReq = false; else breakReq = false; System.out.println("Break required = " + breakReq); }
    void outputBreak() {
        boolean breakReq;
        if (isWeekend())
            if (durationGreaterThan50min())
                breakReq = true;
            else
                breakReq = false;
        else
            breakReq = false;
        System.out.println("Break required = " + breakReq);
    }

It is usual for developers to define multiple return statements in a method that return values from various control statements like if, for, and others. Usually such a method also includes a return statement at the end. Here’s an example, which defines multiple exit points in the method acceptSpeaker() highlighted using end-of-line comments:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
boolean acceptSession(Speaker speaker, Talk talk) {
List<DayOfWeek> days = List.of(DayOfWeek.SUNDAY, DayOfWeek.SATURDAY);
if (speaker.getName().equals("JavaGuru"))
return true; // Exit Point 1
if (talk.getTrack() == Track.SCALA) {
return false; // Exit Point 2
}
for (DayOfWeek day : days) {
if(talk.date.getDayOfWeek().equals(day)) {
return true; // Exit Point 3
}
}
return false;
}
boolean submitTalk(Speaker speaker, Talk talk) {
System.out.println("Received another submission");
boolean isAccepted = acceptSession(speaker, talk);
if (isAccepted) {
// Update website
}
return isAccepted;
}
boolean acceptSession(Speaker speaker, Talk talk) { List<DayOfWeek> days = List.of(DayOfWeek.SUNDAY, DayOfWeek.SATURDAY); if (speaker.getName().equals("JavaGuru")) return true; // Exit Point 1 if (talk.getTrack() == Track.SCALA) { return false; // Exit Point 2 } for (DayOfWeek day : days) { if(talk.date.getDayOfWeek().equals(day)) { return true; // Exit Point 3 } } return false; } boolean submitTalk(Speaker speaker, Talk talk) { System.out.println("Received another submission"); boolean isAccepted = acceptSession(speaker, talk); if (isAccepted) { // Update website } return isAccepted; }
    boolean acceptSession(Speaker speaker, Talk talk) {
        List<DayOfWeek> days = List.of(DayOfWeek.SUNDAY, DayOfWeek.SATURDAY);
        if (speaker.getName().equals("JavaGuru"))
            return true;                                // Exit Point 1
        if (talk.getTrack() == Track.SCALA) {
            return false;				// Exit Point 2
        }
        for (DayOfWeek day : days) {
            if(talk.date.getDayOfWeek().equals(day)) {
                return true;				// Exit Point 3
            }
        }
        return false;
    }

    boolean submitTalk(Speaker speaker, Talk talk) {
        System.out.println("Received another submission");
        boolean isAccepted = acceptSession(speaker, talk);
        if (isAccepted) {
            // Update website
        }
        return isAccepted;
    }

When you inline the method acceptSession() in submitTalk(), IntelliJ IDEA will detect its multiple exit statements and modify the code accordingly:

GIF

Here’s the final code for you to compare (end-of-line comments added for your convenience):

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
boolean submitTalk(Speaker speaker, Talk talk) {
System.out.println("Received another submission");
boolean isAccepted = false;
List days = List.of(DayOfWeek.SUNDAY, DayOfWeek.SATURDAY);
if (speaker.getName().equals("JavaGuru")) {
isAccepted = true; // Assignment
} else {
if (talk.getTrack() != Track.SCALA) {
for (DayOfWeek day : days) {
if (talk.date.getDayOfWeek().equals(day)) {
isAccepted = true; // Assignment
break; // break statement added
}
}
}
}
if (isAccepted) {
// Update website
}
return isAccepted; // single return statement
}
boolean submitTalk(Speaker speaker, Talk talk) { System.out.println("Received another submission"); boolean isAccepted = false; List days = List.of(DayOfWeek.SUNDAY, DayOfWeek.SATURDAY); if (speaker.getName().equals("JavaGuru")) { isAccepted = true; // Assignment } else { if (talk.getTrack() != Track.SCALA) { for (DayOfWeek day : days) { if (talk.date.getDayOfWeek().equals(day)) { isAccepted = true; // Assignment break; // break statement added } } } } if (isAccepted) { // Update website } return isAccepted; // single return statement }
    boolean submitTalk(Speaker speaker, Talk talk) {
        System.out.println("Received another submission");
        boolean isAccepted = false;
        List days = List.of(DayOfWeek.SUNDAY, DayOfWeek.SATURDAY);
        if (speaker.getName().equals("JavaGuru")) {
            isAccepted = true;                       // Assignment 
        } else {
            if (talk.getTrack() != Track.SCALA) {
                for (DayOfWeek day : days) {
                    if (talk.date.getDayOfWeek().equals(day)) {
                        isAccepted = true;           // Assignment
                        break;                       // break statement added
                    }
                }
            }
        }
        if (isAccepted) {
            // Update website
        }
        return isAccepted;                           // single return statement
    }

Inline method with negation

(New in IntelliJ IDEA 2019.2)

With the enhancements in IntelliJ IDEA 2019.2, the Inline Method refactoring also supports negation at call site. In the following code, when you inline the method check() with negation in the method isValidName(), it won’t be modified to a ‘single exit point’:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
boolean check(Speaker speaker) {
if (speaker == null) return false;
String name = speaker.name.trim();
if (name.isEmpty()) return false;
return name.length() % 2 == 0;
}
boolean isValidName(Speaker speaker) {
return !check(speaker);
}
boolean check(Speaker speaker) { if (speaker == null) return false; String name = speaker.name.trim(); if (name.isEmpty()) return false; return name.length() % 2 == 0; } boolean isValidName(Speaker speaker) { return !check(speaker); }
    boolean check(Speaker speaker) {
        if (speaker == null) return false;
        String name = speaker.name.trim();
        if (name.isEmpty()) return false;
        return name.length() % 2 == 0;
    }

    boolean isValidName(Speaker speaker) {
        return !check(speaker);
    }

Here’s how the inlined code is negated – notice the reversal of return values and condition (name.length() % 2 == 0) to (name.length() % 2 != 0):

GIF

Here’s the modified code for you to compare with the original code:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
boolean isValidName(Speaker speaker) {
if (speaker == null) return true;
String name = speaker.name.trim();
if (name.isEmpty()) return true;
return name.length() % 2 != 0;
}
boolean isValidName(Speaker speaker) { if (speaker == null) return true; String name = speaker.name.trim(); if (name.isEmpty()) return true; return name.length() % 2 != 0; }
    boolean isValidName(Speaker speaker) {
        if (speaker == null) return true;
        String name = speaker.name.trim();
        if (name.isEmpty()) return true;
        return name.length() % 2 != 0;
    }

Summary

As developers, we refactor our code often. To refactor our code efficiently, we must know the use cases – why, when, and how to refactor it. IntelliJ IDEA plays a crucial role by automating the various refactoring options for you.

Happy coding and sensible refactoring!

image description

Discover more