Inheritance for builders in lombok - java

I was trying to use lombok for my project.
I have a class A:
#Data
#Builder
public class A {
Integer a1;
}
and a class B:
#Data
public class B extends A {
Integer b1;
#Builder
public B(Integer b1, Integer a1) {
super(a1);
this.b1 = b1;
}
}
I am getting an error saying builder() in B cannot override builder() in A, as return type in BBuilder is not compatible with return type in ABuilder.
Is there some way to do this using lombok?
I do not want to write the complete builder for for B, unless I don't have any other option.
PS: I have given explicit constructor for class B due to Issue.
I tried searching, but I could not find a good solution for the same.

Here we just need to call super of the builder.
#Data
public class B extends A {
Integer b1;
#Builder
public B(Integer b1, Integer a1) {
super(a1);
this.b1 = b1;
}
public static class BBuilder extends ABuilder{
BBuilder() {
super();
}
}
}

If you are using Lombok 1.18.4 along with IntelliJ, following code shall work for you:
#Data
#Builder
class A {
Integer a1;
}
#Data
class B extends A {
Integer b1;
#Builder (builderMethodName = "BBuilder")
public B(Integer b1, Integer a1) {
super(a1);
this.b1 = b1;
}
}
public class Main {
public static void main(String[] args){
System.out.println(B.BBuilder().a1(1).b1(1).build());
}
}
One a side note, #SuperBuilder annotation didn't work in IntelliJ at time of writing this answer. If you have multiple level of inheritance, please avoid Lombok or it will make your Java models messy.

Lombok has introduced experimental features with version: 1.18.2 for inheritance issues faced with Builder annotation, and can be resolved with #SuperBuilder annotation
Please use lombok version: 1.18.2, #SuperBuilder annotations in child/parent class

Both child and parent should be marked with #SuperBuilder.
Having both parent and child as #Builder won't work.
Parent class A:
#Data
#SuperBuilder
public class A {
Integer a1;
}
Child class B:
#Data
#SuperBuilder
public class B extends A {
Integer b1;
}

After hours of hacking at this, I found a viable solution without using the #SuperBuilder. Consider an example -
public class A{
int x;
#Builder(toBuilder = true)
public A(int x){
this.x = x;
}
public static class ABuilder{
protected ABuilder(){} //Note this is important, otherwise BBuilder won't be able to access private no-args constructor of ABuilder
}
}
public class B extends A{
#Builder(builderMethodName="BBuilder", toBuilder=true)
public B(int x){
super(x);
}
public static class BBuilder extends ABuilder{
BBuilder(){
super();
}
}
}
public static void main(){
B obj = new B();
//we can use the existing obj as obj.toBuilder().x(5).build();
//this will return an object of B and not A
}
P.S : I am not sure if #SuperBuilder is an experimental feature still ; so didn't want to take chances

Related

Builder annotates a private class?

#Data
#Builder
public class ClassA {
private ClassB b;
private String createdBy;
private class ClassB {
String name;
int version;
}
}
Hi I want to create ClassA in another java file this way:
ClassA.builder().createdBy("Alex")
.b(ClassB.builder()
.name("Game")
.version(2).build())
.build();
Is this possible for private class classB?
Thx
I recommend as below:
#Data
#Builder
public class ClassA {
private ClassB b;
private String createdBy;
#Builder
#Data
static class ClassB {
String name;
int version;
}
}
if you want unvisible ClassB it is impossible. and To using #Builder on inner class , class must be static class
private modifier means that the variable is only visible for this class only. So if you create private class B inside class A it means B only visible for A.
As #sweeper said in the comment, you cannot use class B in another file / class except for class A.
If you still want to use that way, you can create class B independently.
ClassA.class
#Data
#Builder
public class ClassA {
private ClassB b;
private String createdBy;
}
ClassB.class
#Data
#Builder
class ClassB {
String name;
int version;
}

Lombok constructors and inheritance with final fields [duplicate]

I have a class
#Value
#NonFinal
public class A {
int x;
int y;
}
I have another class B
#Value
public class B extends A {
int z;
}
lombok is throwing error saying it cant find A() constructor, explicitly call it what i want lombok to do is to give annotation to class b such that it generates the following code:
public class B extends A {
int z;
public B( int x, int y, int z) {
super( x , y );
this.z = z;
}
}
Do we have an annotation to do that in Lombok?
This is not possible in Lombok. Although it would be a really nice feature, it requires resolution to find the constructors of the super class. The super class is only known by name the moment Lombok gets invoked. Using the import statements and the classpath to find the actual class is not trivial. And during compilation you cannot just use reflection to get a list of constructors.
It is not entirely impossible but the results using resolution in val and #ExtensionMethod have taught us that is it hard and error-prone.
Disclosure: I am a Lombok developer.
Lombok Issue #78 references this page https://www.donneo.de/2015/09/16/lomboks-builder-annotation-and-inheritance/ with this lovely explanation:
#AllArgsConstructor
public class Parent {
private String a;
}
public class Child extends Parent {
private String b;
#Builder
public Child(String a, String b){
super(a);
this.b = b;
}
}
As a result you can then use the generated builder like this:
Child.builder().a("testA").b("testB").build();
The official documentation explains this, but it doesn’t explicitly point out that you can facilitate it in this way.
I also found this works nicely with Spring Data JPA.
Version 1.18 of Lombok introduced the #SuperBuilder annotation. We can use this to solve our problem in a simpler way.
You can refer to https://www.baeldung.com/lombok-builder-inheritance#lombok-builder-and-inheritance-3.
so in your child class, you will need these annotations:
#Data
#SuperBuilder
#NoArgsConstructor
#EqualsAndHashCode(callSuper = true)
in your parent class:
#Data
#SuperBuilder
#NoArgsConstructor
Lombok does not support that also indicated by making any #Value annotated class final (as you know by using #NonFinal).
The only workaround I found is to declare all members final yourself and use the #Data annotation instead. Those subclasses need to be annotated by #EqualsAndHashCode and need an explicit all args constructor as Lombok doesn't know how to create one using the all args one of the super class:
#Data
public class A {
private final int x;
private final int y;
}
#Data
#EqualsAndHashCode(callSuper = true)
public class B extends A {
private final int z;
public B(int x, int y, int z) {
super(x, y);
this.z = z;
}
}
Especially the constructors of the subclasses make the solution a little untidy for superclasses with many members, sorry.
for superclasses with many members I would suggest you to use #Delegate
#Data
public class A {
#Delegate public class AInner{
private final int x;
private final int y;
}
}
#Data
#EqualsAndHashCode(callSuper = true)
public class B extends A {
private final int z;
public B(A.AInner a, int z) {
super(a);
this.z = z;
}
}
If child class has more members, than parent, it could be done not very clean, but short way:
#Data
#RequiredArgsConstructor
#EqualsAndHashCode(callSuper = true)
#ToString(callSuper = true)
public class User extends BaseEntity {
private #NonNull String fullName;
private #NonNull String email;
...
public User(Integer id, String fullName, String email, ....) {
this(fullName, email, ....);
this.id = id;
}
}
#Data
#AllArgsConstructor
abstract public class BaseEntity {
protected Integer id;
public boolean isNew() {
return id == null;
}
}
As a workaround while there is no proper annotation, you could use com.fasterxml.jackson.databind.ObjectMapper to initialize a child class from the parent
public class A {
int x;
int y;
}
public class B extends A {
int z;
}
ObjectMapper MAPPER = new ObjectMapper(); //it's configurable
MAPPER.configure( DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false );
MAPPER.configure( SerializationFeature.FAIL_ON_EMPTY_BEANS, false );
//Then wherever you need to initialize child from parent:
A parent = new A(x, y);
B child = MAPPER.convertValue( parent, B.class);
child.setZ(z);
You can still use any lombok annotations on A and B if you need.

Best way to write pojos that can have common fields

I am currently making a service in which there are lots of public API's. And the response and request objects overlap a lot. So, I was thinking that is there a way by which we can generalise the pojo creation for the request/response objects.
Sometimes the response object is identical to the request object with one or two extra fields.
Let me give you an example.
#Data
public class Request {
private A objA;
private B objB;
}
#Data
public class Response {
private A objA;
private B objB;
private C objC;
}
#Data
public class A {
private D objD;
}
#Data
public class B {
private String sB;
private E obje;
}
#Data
public class C {
private String sC;
}
Similary, D and E are pojos as well. The thing is that there is a lot of similarity(overlapping fields) in request/response objects.
Your solution is probably inheritance: Create a parent abstract object type with the overlapping fields and have the request and response objects extend it and specify any extra (unique) fields they need.
Inheritence
public abstract class Common {
private String overlapfield1;
private String overlapfield2
}
public class Request extends Common {
private String requestField1;
private String requestField2;
}
public class Response extends Common {
private String responseField1;
private String responseField2;
}
You could also approach this using composition: Create an object type with the overlapping fields and include this object as a sub-object of the Request/Response types:
Composition
public class Common {
private String overlapfield1;
private String overlapfield2
}
public class Request {
private String requestField1;
private String requestField2;
private Common common;
}
public class Response {
private String responseField1;
private String responseField2;
private Common common;
}
There are pros and cons to each approach which are widely discussed on this and other boards. These however, are the two standard approaches to dealing with such a problem.
It really depends on what you are trying to achieve. I don't see it being a huge problem repeating the fields but you've given an abstract use case rather than a real world situation where I can understand what you're trying to achieve.
Perhaps you want to pass your #Data objects to the same services? In which case you might want to use interfaces because a class can implement multiple interfaces.
Eg
public interface AContiner {
A getA();
void setA(A a);
}
public interface BContiner {
B getB();
void setB(B b);
}
#Data
public class Bean1 implements AContainer {
private A a;
}
#Data
public class Bean2 implements AContainer, BContainer {
private A a;
private B b;
}
public class MyFantasticService {
public void doStuffWithA(AContainer data) {
System.out.println(data.getA());
}
public void doStuffWithB(BContainer data) {
System.out.println(data.getB());
}
}

How to avoid duplicate code in the translation objects?

I have a super class named Transformer. and I have two derived classes that converts class A to B and vice versa. There is the classes :
class A{
private Long id;
private String name;
... getter and setters
}
class B{
private Long id;
private String name;
... getter and setters
}
interface Transformer<S,D> {
D convert(S source);
}
class ATransformer implements Transformer<A,B>{
public B convert(A a){
B b=new B();
b.setId(a.getId());
.....
return b;
}
}
class BTransformer implements Transformer<B,A>{
public A convert(B b){
A a=new A();
a.setId(b.getId());
.....
return a;
}
}
as you see, derived transformer classes have duplicate codes. Is there any design or solution to avoid this duplication without using reflection?
You can use a bean mapper framework like Dozer or orika to do the work for you (and spare you the duplicate code).
However as already mentioned in the other comments, it is (in most cases) preferable to avoid having classes that are nearly the same without them being in the same class hirarchy and therefore castable.

how to know the subclass of mapping in hibernate

#Entity
#Inheritance(strategy = InheritanceType.JOINED)
public class A{
private long id;
}
#Entity
public class B extends A{
private String bProperty;
}
#Entity
public class C extends A{
private String cProperty;
}
#Entity
public class Person{
#OneToMany
private Set<A> a;
}
when I use person.getVehicles
How can I know the A is B or C?
I'm using instanceof to check and cast it to get bProperty or cProperty.
Is there any other better way?
The only safe way is to use a polymorphic method. Even instanceof will not work because the instance might actually be a proxy, i.e. a subclass of A that is neither a B or a C, but delegates to a B or a C.
public class A{
private long id;
public abstract boolean isB();
public abstract boolean isC();
public abstract String getBProperty();
public abstract String getCProperty();
}
public class B extends A{
private String bProperty;
public boolean isB() {
return true;
}
public boolean isC() {
return false;
}
public String getBProperty() {
return bProperty;
}
public String getCProperty() {
throw new IllegalStateException("I'm not a C");
}
}
To be cleaner, try using the visitor pattern. I've written a blog post about it. It's in French, but it should be easily translatable.

Categories