I have following code:
#Slf4j
public class Main {
#Data
public static class AClass {
private String title;
}
#Data
public static class BClass extends AClass {
private Integer amount;
}
public static void main(String[] args) {
BClass bClass = new BClass();
bClass.setAmount(12);
bClass.setTitle("title");
log.info("{}", bClass);
}
}
In logs I can see only fields from BClass and not AClass :
17:52:28.151 [main] INFO Main - Main.BClass(amount=12)
But I want to see all BClass fields including ones that are in parent class(title field). How can I do it?
That's normal - what you're looking at (specifically, the Main.BClass(amount=12) part in your log) is the output of the toString() method of BClass. The #Data annotation is making a toString implementation for you. However, lombok intentionally does not include superclass information normally. You should be seeing a warning on your #Data annotation that explains this. One way to solve that problem is to add #ToString(callSuper = true) next to your #Data annotation.
This isn't about SLF4J or any logging framework - it's about what is and isn't in the generated toString() implementation.
Related
I want to create a new Child instance passing a Parent and other additional parameters.
For example if I have:
public class Parent {
public String param1;
public String param2;
// many parameters
public String paramN;
}
public class Child extends Parent {
public String subValue;
}
With lombok, is there a builder that lets me create a Child instance passing the Parent and the missing value as parameters?
Would be easier if I could write something like:
Parent p = Parent.builder()
.param1("a")
.param2("b")
// many parameters
.paramN("b")
.build();
Child c = Child.builder(p).subValue("c").build();
Other answers don't truly make your client code simply reuse the parent instance you already have. But this is doable. You have two options:
The hard one is to write your custom annotation that does what you want. You can even make it generic so that it works for any classes the have parent/child hierarchy. Have a look at this example. If you feel brave you can raise a feature request on Lombok's github page.
Option two would be to write your custom builder for the child. See example here. In your custom builder in the init step you would be reading a passed in Parent instance, and setup the inherited fields only.
The regular #Builder is not sufficient here, because you are dealing with a class hierarchy. However, #SuperBuilder was made exactly for such a case.
#SuperBuilder generates complex code loaded with generics. That makes this solution difficult to understand without in-depth knowledge about the code #SuperBuilder generates. You should think about whether this is worth it.
Here's the solution (with Lombok >= 1.18.16):
#SuperBuilder(toBuilder = true)
public static class Parent {
public String param1;
public String param2;
// many parameters
public String paramN;
public abstract static class ParentBuilder<C extends Parent, B extends Parent.ParentBuilder<C, B>> {
protected B $fillValuesFromParent(Parent instance) {
$fillValuesFromInstanceIntoBuilder(instance, this);
return self();
}
}
}
#SuperBuilder(toBuilder = true)
public static class Child extends Parent {
public String subValue;
public static ChildBuilder<?, ?> toBuilder(Parent p) {
return new ChildBuilderImpl().$fillValuesFromParent(p);
}
}
The new toBuilder method on Child creates a new ChildBuilderImpl (which will create a Child instance when calling build()). To fill the values from the given Parent p, it calls the new $fillValuesFromParent method from ParentBuilder. This method further delegates the call to the method $fillValuesFromInstanceIntoBuilder, which is generated by Lombok and performs the actual copying of the field values to the new builder instance.
Also note the $ prefix on the methods. This basically says: I'm an implementation detail; don't use me unless you know what you are doing, I might break on the next Lombok version without further notice.
I would suggest you use #SuperBuilder
#SuperBuilder was introduced as experimental feature in lombok v1.18.2.
The #SuperBuilder annotation produces complex builder APIs for your
classes. In contrast to #Builder, #SuperBuilder also works with fields
from superclasses. However, it only works for types. Most importantly,
it requires that all superclasses also have the #SuperBuilder
annotation.
#Getter
#SuperBuilder
public class Parent {
public String name;
public String value;
}
#Getter
#SuperBuilder
public class Child extends Parent {
public String subValue;
}
Then all you need to do is
Child.builder().name("a").value("b").subValue("c").build();
I find #AllArgsConstructor(onConstructor = #__(#Inject)) is helpful to keep code clean when working with Google Guice. I can save the constructor code. For example:
#AllArgsConstructor(onConstructor = #__(#Inject))
public class SomeClass {
private final DependentClassOne classOne;
private final DependentClassTwo classTwo;
// ...
}
For abstract class, I'm able to use #Inject for constructor.
#AllArgsConstructor(onConstructor = #__(#Inject))
public abstract class AbstractParentClass {
private final DependentClassOne classOne;
}
public class ChildClass extends AbstractParentClass {
private final DependentClassTwo classTwo;
#Inject
public ChildClass(final DependentClassOne classOne, final DependentClassTwo classTwo) {
super(classOne);
this.classTwo = classTwo;
}
}
Is it possible to save the constructor code in ChildClass by using something like #AllArgsConstructor(onConstructor = #__(#Inject))?
No, it is not possible to define AllArgsConstructor in child class when there is parent constructor due to the limitation of Lombok (see this issue on GitHub and another answer on SO).
You could mix field/setter injection in parent with constructor injection in child, but I would advise to avoid it.
I want to create a Lombok class inside a test class
#RunWith(SpringRunner.class)
#SpringBootTest
public class HostelIntegrationTest {
#Data
#Builder
#NoArgsConstructor
#AllArgsConstructor
#JsonInclude(NON_NULL)
#EqualsAndHashCode
class User {
String property1;
Instant property2;
Integer property3;
}
but I get this compilation error:
modifier static not allowed here
#Builder makes a static internal class inside.
The problem is probably the static internal class inside the non-static internal class.
Try to make User also static
//other annotations
#Builder
static class User {
String property1;
Instant property2;
Integer property3;
}
Defining your inner class as static will solve this.
Background: every instance on an inner class will have a reference to the object of the outer class that created it, unless the inner class ist defined as static. Usually you will not need that reference, that's why you should define your inner classes as static (this is a good static even from the PoV of OOP unlike static methods and fields).
Lombok #Builder will define a static method in your inner class (builder()), that's only allowed in static inner classes.
I have 2 classes:
import lombok.Builder;
#Builder
public class B extends A {
}
and
import lombok.Builder;
#Builder
public class A {
}
on the #Builder on B I get the message:
The return type is incompatible with A.builder().
Is this a limitation of lombok? or something I'm doing wrong?
If I leave the #Builder off A, then the Builder on B doesn't seem to consider the fields in A in the constructors for B.
The latest lombok release 1.18.2 includes the new experimental #SuperBuilder. It supports inheritance and fields from superclasses (also abstract ones). The only requirement is that all superclasses must have the #SuperBuilder annotation. With it, the solution is as simple as this:
#SuperBuilder
public class B extends A {
private String b;
}
#SuperBuilder
public class A {
private String a;
}
B instance = B.builder().b("b").a("a").build();
It is only possible with a workaround (See #78)
From Reinhard.codes
We have been using #Builder on the class itself, but you can also put it on a class’s constructor or on a static method. In that case, Lombok will create a setter method on the builder class for every parameter of the constructor/method. That means you can create a custom constructor with parameters for all the fields of the class including its superclass.
#AllArgsConstructor
public class Parent {
private String a;
}
public class Child extends Parent {
private String b;
#Builder
private Child(String a, String b){
super(a);
this.b = b;
}
}
faced issue using lombok with java inheritance, resolved after using the below annotations on parent and child class:
#EqualsAndHashCode(callSuper = true)
#SuperBuilder
#Data
#AllArgsConstructor
#NoArgsConstructor
I cannot reproduce your exact problem anymore, but this may be because Lombok has evolved.
Part of your question, however, was that the builder for be does not include the fields for a. That remains true, as is also for #AllArgsConstructor. Inheritance is not Lombok’s strong suit.
Thad said, since you can write your constructor yourself and can put #Builder on the constructor, the following will generate a builder for B just as you wished:
#Builder
public class A {
String a;
}
public class B extends A {
#Builder
B(String a, String b) {
super(a);
this.b = b;
}
String b;
}
Without knowing the implementation details of lombok or trying it out i'd say no because the pattern won't allow it.
If you implement the builder pattern all of your methods (except of build() ) will always have the class which the builder exists for as return type.
That means class A's methods will only return A. So does B always return B.
If you now let B extend from A it will not override A's methods because it's return type does not match. Vice versa it cannot implement the builder methods in B because those methods already exist in A. They cannot coexist by OOP design.
You may be able to create a generic builder but that does not solve the problem. If you really need to extend from A you're problem may come from another design decision which the builder pattern cannot solve.
I'd assume that instead of extending the class you'd have default values in your builder which lombok should support. Those default values then reflect what class A may support by default. In a use-case where you'd rather have B doing stuff you'd then call the builder methods and override those default values.
Edit: Oh and maybe have a look here
Lombok #Builder doesn't work for inheritance use cases:
For example
class Foo{
protected int xyz1;
.....
protected String xyz7;
}
class Bar extends Foo{
}
For given use case Lombok will not be able to generate methods to set value of parameter defined in Foo class.
A workaround for this is:
Manual creating constructor of Bar.
Putting a Builder annotation on that constructor.
Is there a better workaround ?
Lombok has introduced experimental features with version: 1.18.2 for inheritance issues faced with Builder annotation, and can be resolved with #SuperBuilder annotation as below.
#SuperBuilder
public class ParentClass {
private final String a;
private final String b;
}
#SuperBuilder
public class ChildClass extends ParentClass{
private final String c;
}
Now, one can use Builder class as below (that was not possible with #Builder annotation)
ChildClass.builder().a("testA").b("testB").c("testC").build();
I leave this here for reference, as other answers demonstrate there is now (not at the time this answer was posted) a #SuperBuilder feature available now in the library which seems more fitting.
It´s a bit hidden, but people have had this question before, see:
https://reinhard.codes/2015/09/16/lomboks-builder-annotation-and-inheritance/
To summarise the blog post
#AllArgsConstructor
public class Parent {
private String a;
}
public class Child extends Parent {
private String b;
#Builder
private Child(String a, String b){
super(a);
this.b = b;
}
}
Would allow you to use
Child.builder().a("testA").b("testB").build()
There is a solution to this problem currently in the works. Check the progress here: https://github.com/rzwitserloot/lombok/pull/1337