31

I am using Lombok library in my project and I am not able to use a class annotated with @Builder in outer packages.

Is there a way to make the builder public?

MyClass instance = new MyClass.MyClassBuilder().build();

The error is:

'MyClassBuilder()' is not public in 'com.foo.MyClass.MyClassBuilder'. Cannot be accessed from outside package

Michael
  • 41,989
  • 11
  • 82
  • 128
H.abidi
  • 579
  • 1
  • 7
  • 16

5 Answers5

52

@Builder already produces public methods, it's just the constructor that's package-private. The reason is that they intend for you to use the static builder() method, which is public, instead of using the constructor directly:

Foo foo = Foo.builder()
    .property("hello, world")
    .build();

If you really, really, really want the constructor to be public (there seems to be some suggestion that other reflection-based libraries might require it), then Lombok will never override anything that you've already declared explicitly, so you can declare a skeleton like this with a public constructor and Lombok will fill in the rest, without changing the constructor to package-private or anything.

@Builder
public class Foo
{
    private final String property;

    public static class FooBuilder
    {
        public FooBuilder() { }

        // Lombok will fill in the fields and methods
    }
}

This general strategy of allowing partial implementations to override default behaviour applies to most (maybe all) other Lombok annotations too. If your class is annotated with @ToString but you already declared a toString method, it will leave yours alone.

Just to show you everything that gets generated, I wrote the following class:

@Builder
public class Foo
{
    private final String property;
}

I then ran it through delombok to see everything that was generated. As you can see, everything is public:

public class Foo
{
    private final String property;

    @java.beans.ConstructorProperties({"property"})
    Foo(final String property) {
        this.property = property;
    }

    public static FooBuilder builder() {
        return new FooBuilder();
    }

    public static class FooBuilder
    {
        private String property;

        FooBuilder() { }

        public FooBuilder property(final String property) {
            this.property = property;
            return this;
        }

        public Foo build() {
            return new Foo(property);
        }

        public String toString() {
            return "Foo.FooBuilder(property=" + this.property + ")";
        }
    }
}
Michael
  • 41,989
  • 11
  • 82
  • 128
18

The problem is you are using @Builder in the wrong way.

When Builder Pattern is used, you only need to use the static method to invoke it and then build, for example:

MyClass instance = MyClass.builder().build(); .

Please do not new the MyClassBuilder again, it breaks the encapsulation the pattern has since you are directly using the inner MyClassBuilder class. This constructor is been hided from outside, that's why you get the not accessible error. Instead it provides you the static method builder().

J.Luan
  • 227
  • 2
  • 5
  • 1
    Thats not the question. If we need to use Builder class explicitly within masptruct, this is a huge pain. The generated constructor is package private – Ashok Koyi May 06 '19 at 16:05
  • As of now, I'm getting around this problem for mapstruct by using ```@ObjectFactory public UserResponse.UserResponseBuilder resolveBuilder() { return UserResponse.builder(); }``` – Ashok Koyi May 06 '19 at 16:14
2

I have found this neat workaround:

import lombok.Builder;
import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
@Builder
public class Customer {
    private String id;
    private String name;

    public static MessageBuilder builder() {return new CustomerBuilder();}
}
Brad
  • 958
  • 8
  • 21
1

The problem with this builder annotation is that, if you delombok you'll see, the generated constructor for the builder has no access indicator (public, private, protected) therefore is only visible within the same package. This would work if the extended classes were in the same package.

I'm having the same problem and I think that lombok does not support this, for now. I was able to find the feature request in here https://github.com/rzwitserloot/lombok/issues/1489

My suggestion is to hard implement builder pattern in this class.

0

as mentioned you can use the builder, now instead of user property builder() will return the instance create so you can treat as normal builder ( no need to use property)

instance = MyClass.MyClassBuilder().property1(value1).property1(value2).build();
Amer Qarabsa
  • 6,412
  • 3
  • 20
  • 43