Javadoc "cannot find symbol" error when using Lombok's @Builder annotation
Asked Answered
W

5

46

I have a class looking as below :

@Data
@Builder
public class Foo {
    private String param;

    /** My custom builder.*/
    public static FooBuilder builder(String _param){
        return builder().param(_param);
    }
}

I get the following error :

[ERROR] Failed to execute goal org.apache.maven.plugins:maven-javadoc-plugin:2.10.4:javadoc (default-cli) on project foo: An error has occurred in JavaDocs report generation:
[ERROR] Exit code: 1 - /home/workspace/foo/src/main/java/com/foo/Foo.java:34: error: cannot find symbol
[ERROR] public static FooBuilder builder(String _param)
[ERROR] ^
[ERROR] symbol: class FooBuilder
[ERROR] location: class Foo

Wrongly answered 21/8, 2018 at 11:32 Comment(0)
W
26

In order to solve this issue, I have to use Lombok's delombok feature (cf : https://projectlombok.org/features/delombok).

lombok doesn't cover all tools. For example, lombok cannot plug into javadoc ... which run on java sources. Delombok still allows you to use lombok with these tools by preprocessing your java code into java code with all of lombok's transformations already applied.

I did this using Maven by adding the following plugins :

<plugin>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok-maven-plugin</artifactId>
    <version>1.18.0.0</version>
    <configuration>
        <sourceDirectory>${project.basedir}/src/main/java</sourceDirectory>
        <outputDirectory>${delombok.output}</outputDirectory>
        <addOutputDirectory>false</addOutputDirectory>
    </configuration>
    <executions>
        <execution>
            <phase>generate-sources</phase>
            <goals>
                <goal>delombok</goal>
            </goals>
        </execution>
    </executions>
</plugin>
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-javadoc-plugin</artifactId>
    <version>2.9</version>
    <configuration>
        <sourcepath>${delombok.output}</sourcepath>
    </configuration>
</plugin>
Wrongly answered 21/8, 2018 at 11:32 Comment(3)
Is there no other way to process annotations such as @Builder with javadoc-plugin? Same as the compiler-plugin has <annotationProcessorPaths>? Using delombok sounds like an anti-pattern.Disfrock
Yes it sounds, but I don't think there is an other way, even lombok documentation asserts it (cf. my answer),Wrongly
The default delombok.outputDirectory is ${project.build.directory}/generated-sources/delombok so you only need to specify that in the maven-javadoc-pluginPreserve
S
39

Lombok is actually capable of filling out a partially defined builder class, so you can declare enough of the builder to make Javadoc happy and leave it at that. No need to delombok.

The following worked for me in this situation:

@Data
@Builder
public class Foo {
    private String param;

    // Add this line and all is good
    public static class FooBuilder {}

}

Side note: that you can actually use this technique to add some customer builder methods, so it has perks. I like to overload builder methods when I have collections so I can items one at a time. There's probably already some technique that does that, but it's nice to know you can improve the builders manually.

Here's a common thing I like to do:

@Builder
public class Foo {
    private final String command;
    private final List<String> params;
    private final boolean background;
    
    public static class FooBuilder {
        public FooBuilder params(final String... params) {
            this.params = Arrays.asList(params);
            return this;
        }
    }
}

In the above the params builder method has been customized to take var args. The other builder method will still be created by Lombok.

Significs answered 11/11, 2019 at 22:19 Comment(3)
THX! Works fine (JDK 8, Lombok 1.8.12, Mapstruct 1.3.1) and saves us from Delombok :-)Singlefoot
I just hit this problem, and found that only the declaration for the empty FooBuilder was needed. One can drop the larger builder(..) function. (JDK17, Lombok 1.18.22).Lebar
@ChrisK Correct. I added the additional builder method as an example of the customization mentioned in the side note. I'll clarify so it's clear what the minimum is.Significs
W
26

In order to solve this issue, I have to use Lombok's delombok feature (cf : https://projectlombok.org/features/delombok).

lombok doesn't cover all tools. For example, lombok cannot plug into javadoc ... which run on java sources. Delombok still allows you to use lombok with these tools by preprocessing your java code into java code with all of lombok's transformations already applied.

I did this using Maven by adding the following plugins :

<plugin>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok-maven-plugin</artifactId>
    <version>1.18.0.0</version>
    <configuration>
        <sourceDirectory>${project.basedir}/src/main/java</sourceDirectory>
        <outputDirectory>${delombok.output}</outputDirectory>
        <addOutputDirectory>false</addOutputDirectory>
    </configuration>
    <executions>
        <execution>
            <phase>generate-sources</phase>
            <goals>
                <goal>delombok</goal>
            </goals>
        </execution>
    </executions>
</plugin>
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-javadoc-plugin</artifactId>
    <version>2.9</version>
    <configuration>
        <sourcepath>${delombok.output}</sourcepath>
    </configuration>
</plugin>
Wrongly answered 21/8, 2018 at 11:32 Comment(3)
Is there no other way to process annotations such as @Builder with javadoc-plugin? Same as the compiler-plugin has <annotationProcessorPaths>? Using delombok sounds like an anti-pattern.Disfrock
Yes it sounds, but I don't think there is an other way, even lombok documentation asserts it (cf. my answer),Wrongly
The default delombok.outputDirectory is ${project.build.directory}/generated-sources/delombok so you only need to specify that in the maven-javadoc-pluginPreserve
J
4

Update

If you use maven-javadoc-plugin 3.2.0+ you can configure it like this:

  <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-javadoc-plugin</artifactId>
    <version>3.2.0</version>
    <configuration>
      <doclint>none</doclint>
    </configuration>
  </plugin>

The doclint configuration will make javadoc plugin not throw an error anymore. It will also disable the lint but if you are ok with this probably the best way to go instead of delombok.

If you use any CI tool to build and compile your project you can create a separated job to check for javadoc lint.

For me disabling lint in the build is not a bad thing. Javadoc is important but shouldn't keep me from building my application just because I'm using Lombok.

Joyajoyan answered 3/7, 2021 at 0:32 Comment(2)
Doesn't work in JDK 20Orbital
Doesn't work if use something like @RequiredArgsConstructor(onConstructor = @__(@Inject))Biblio
L
2

This is most likely not your case. But the only situation I've ever had this happen, is if I had some other problem in my build. A common issue would be having mapstruct, that needs to generate classes, but it fails. And the build stopped before lombok managed to generate whatever it generates.

Then you get build errors when referencing the builder, because it never got built. However it's not a lombok issue, it's a build issue.

Again, probably not your case. But I've never seen this issue otherwise.

Left answered 15/8, 2023 at 15:25 Comment(0)
A
1

Another solution to this would be to not include the BuilderClass in your imports. Instead just import the parent class and change your declaration of the builder type to parentClass.builderClass.

@Getter
@RequiredArgsConstructor
@Builder
public class Foo {
    private final String param;
}
import com.Foo;
//import com.Foo.FooBuilder;

public class Bar {

    public Foo newFoo(String paramValue) {
        Foo.FooBuilder builder = Foo.builder();
        return builder.param(paramValue)
                  .build();
    }

}
Aiguillette answered 9/12, 2022 at 17:21 Comment(1)
This will work when running into an issue in another class. Looking closer at the original question though, the Builder is being used in the class the builder is defined in. In that case this doesn't work and you would need to use @DavidBlevins solution.Aiguillette

© 2022 - 2024 — McMap. All rights reserved.