Home » Java » IDE refactoring support in a Lombok project

IDE refactoring support in a Lombok project

Posted by: admin December 28, 2021 Leave a comment

Questions:

Project Lombok is tempting to reduces boilerplate code in our Java 8 code base. The downside is that it limits tool support (refactoring, static analysis).

For example, in my experiments with IntelliJ, refactoring of fields of a class annotated with @Builder, no longer works. I know of no workaround (you have to manually fix locations, where the old method name of the Builder is used).

Another example is that in Eclipse “find references” on a field does not find the references, but a good workaround is to open the outline and apply “find references” on the generated getter/setter.

My questions:

  • Which refactoring features of the major IDEs (especially Eclipse, IntelliJ) does it break?
  • Are there good workarounds?
Answers:

Here’s a small workaround to refactor the getter/setter of a variable in a @Data class. This works in eclipse and probably elsewhere as well:

Sample class, where we want to refactor “value” to “value2”:

import lombok.Data;
@Data
public class Thing {
    int value;
}

(1) Rename (don’t refactor) the variable to something temporary to remove lombak’s generated getter/setter for the original name. You’ll get compile errors wherever the old getter/setter were referenced, but that is temporary:

@Data
public class Thing {
    int valueXXX; // reference to getValue() are broken for the moment
}

(2) Manually create a dummy getter/setter for the old name. Your compile errors will go away now:

@Data
public class Thing {
    int valueXXX;
    public int getValue() { return 0; }
    public void setValue(int value) {}
}

(3) Use eclipse to refactor your dummy getter/setter. All references in your codebase now use getValue2() and setValue2():

@Data
public class Thing {
    int valueXXX; // 
    public int getValue2() { return 0; }
    public void setValue2(int value) {}
}

(4) Delete the renamed dummy getter/setter and change the variable name from your temporary name to the new one. Now it’s all lombakized again:

@Data
public class Thing {
    int value2;
}

Admittedly this is a bit annoying, but it doesn’t actually take that long and it sure beats changing hundreds of references by hand.

###

One I recently came across:

In IntelliJ (don’t know about Eclipse), you can’t extract an interface that includes any methods generated by lombok. They don’t show up on the relevant dialog.

There is an easy workaround: Let IntelliJ create the methods, extract the interface, revert your class and have it implement the interface again.

###

For more complicated refactorings, I have settled on “delombok-refactor-relombok”. This is a heavy weight approach, but has the benefits handling complex refactorings without intermediate broken builds.

In my case, I use maven for builds. I added the maven build plugin for lombok:delombok configured as follows:

<build>
    <plugins>
        <plugin>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok-maven-plugin</artifactId>
            <version>1.18.6.0</version>
            <configuration>
                <sourceDirectory>${project.basedir}/src/main/java</sourceDirectory>
                <addOutputDirectory>>false</addOutputDirectory>
            </configuration>
            <executions>
                <execution>
                    <phase>generate-sources</phase>
                    <goals>
                        <goal>delombok</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

This makes delombok copies easily available under the target directory.

To refactor a specific lombok class:

  1. Comment out the original class content
  2. Replace with delombok class content from target/generated-sources/delombok
  3. Save (code compiles cleanly)
  4. Perform refactoring in your IDE (code compiles cleanly)
  5. Perform same refactoring on commented out original lombok class content
  6. Remove delomok class content
  7. Uncomment refactored lombok class content
  8. Save (code compiles cleanly)

I would expect skepticism for those looking at this for their first attempt to refactor a lombok class. I arrived here only after frustrations and limitations with other techniques and more complex lombok features (i.e. @Builder, @SuperBuilder).

For example, manually adding the setter for a @Builder requires declaring the matching lombok generated inner builder class correctly. This become more difficult to do with features like lombok @SuperBuilder builder inheritance. When I attempted to do this manually, I decided to look at the delombok class as a guide. That is when I realized it was simpler to substitute the delombok source, refactor at will, and then remove it.