Reputation: 585
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
Upvotes: 30
Views: 27048
Reputation: 51
You can simply change access level using
@Builder(access = AccessLevel.PUBLIC)
Upvotes: 2
Reputation: 44150
@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 + ")";
}
}
}
Upvotes: 54
Reputation: 287
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()
.
Upvotes: 22
Reputation: 1128
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();}
}
Upvotes: 2
Reputation: 113
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.
Upvotes: 2