Lombok constructors and inheritance with final fields [duplicate] - java

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.

Related

JAXB unmarshal child attributes without creation of child class

I want to unmarshal a (simplified) XML structure like this:
<parent>
<a>AValue</a>
<b>BValue</b>
<c someAttribute = "true">CValue</c>
</parent>
I know how to do this with declaring a class C like this:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "c", propOrder = {
"someAttribute"
})
public class C{
#XmlValue
private String c;
#XmlAttribute ( name="someAttribute")
private boolean someAttribute;
//getters and setters
}
And getting it as a member in class parent like this:
public class Parent{
private String a;
private String b;
private C c;
//getters and setters for c,b,a
}
This works finde and i can access the value of C via parent.getC().getC();
My Question is how to achieve that i do not have to create a class C and get the value and attribute of C as a member of parent, without editing the parent Pojo with new members and other getters and setters.
I already tried to do this via Listeners and searched for similar structures, but i haven't got any ideas left.
I finally figured out how to achieve this.
Its necessary to use the #XmlJavaTypeAdapter Annotation and mark the C class as an #XmlRootElement as well as an #XmlAccessorType(XmlAccessType.FIELD).
Furthermore one need to use the #XmlTransient on the getter of the String member which was annotated with #XmlJavaTypeAdapter.
Full solution:
Class C:
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class C{
#XmlValue
private String c;
#XmlAttribute
private boolean someAttribute;
//getters and setters for both
Class Adapter:
public class Adapter extends XmlAdapter<C, String> {
public String unmarshal(C pC) throws Exception {
//some possible handling with the attribute over pC.getSomeAttribute();
return pC.getC();
}
public C marshal(String pC) throws Exception {
C c = new C();
c.setC(pC)
//some possible handling to set the attribute to c
return c;
}
Class Parent:
public class Parent{
private String a;
private String b;
#XmlJavaTypeAdapter(Adapter.class)
private String c;
#XmlTransient
public String getC() {
return c;
}
//getters and setters for b,a and setter for C
}

Inheritance for builders in lombok

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

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.

Hibernate annotation for base java class

I would like to put into db a class that have java.awt.geom.Point2D field. Is it possible?
Here is my code.
#Entity
#Table(name = "my_class_table")
public class MyClass {
private String aliasId;
private Point2D field;
public Point2D getField() {
return field;
}
public void setFieldPoint2D field) {
this.field = field;
}
public String getAliasId() {
return aliasId;
}
public void setAliasId(String aliasId) {
this.aliasId = aliasId;
}
}
And the reason of the exception which is thrown:
Could not determine type for: java.awt.geom.Point2D, at table: my_class_table, for columns: [org.hibernate.mapping.Column(field)]
Of course, the reason is quite obvious. My question is: how should I annotate the class to be able to use a field of Point2D class? Is it possible at all?
The simplest way is to use a java.awt.Point that extends Point2D and is a Serializable class. This way hibernate will automatically map it with SerializableType and you don't need to do anything more. The point object will be saved in its serialized form in a blob database table column.
You have also the option to define a custom hibernate type for the Point2D class. Here is a link of how to define a custom hibernate type.
You can't add annotations to existing classes.
But you can define a CompositeUserType to tell Hibernate how to map a Point2D.
Thanks guys for response. Unfortunatelly java.awt.Point class uses Integer, so it is useless in my case. The easiest way to solve it would be to use Point2D.Double which implements Serializable (but definition of UserType or CompositeUserType is more convenient if you don't want to change class definition). So, the simple solution:
#Entity
#Table(name = "my_class_table")
public class MyClass {
private String aliasId;
private Point2D.Double field;
public Point2D.Double getField() {
return field;
}
public void setField(Point2D.Double field) {
this.field = field;
}
public String getAliasId() {
return aliasId;
}
public void setAliasId(String aliasId) {
this.aliasId = aliasId;
}
}
But my final goal was to create a class with ordered list of points. If anybody is interested here is an example of the class representing line:
#Entity
public class Line {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "idDb", unique = true, nullable = false)
private int id;
#ElementCollection
#CollectionTable(name="points_table", joinColumns = #JoinColumn(name="idDb"))
#IndexColumn(name = "idx")
#Column(name="point_val")
private List<Point2D.Double> points = new ArrayList<Point2D.Double>();
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public List<Point2D.Double> getPoints() {
return points;
}
public void setPoints(List<Point2D.Double> points) {
this.points = points;
}
}

JPA #Entity Inheritance

I have been looking into JPA/Hibernate #Entity inheritance for a while now and can't seem to find anything that addresses what I am trying to achieve.
Basically I want to be able to define an #Entity with all of the column and table mappings as required. Then I want to be able to extend the #Entity in a number of different locations with different sets of #Transient methods defined in the body of each "sub-Entity". This is a basic example of what I am trying to achieve but with no success thus far:
#Entity
#Table(name = "mountain")
public class MountainEntityBase implements Serializable {
public Integer mountainId = 0;
public Integer height = 0;
public List<ExplorerEntityBase> explorers = new ArrayList<ExplorerEntityBase>();
#Id
#GeneratedValue
#Column(name = "mountain_id")
public Integer getMountainId() { return mountainId; }
public void setMountainId(Integer mountainId) { this.mountainId = mountainId; }
#Column(name="height")
public String getHeight() { return height; }
public void setHeight(String height) { this.height = height; }
#OneToMany(mappedBy="mountainId")
public List<ExplorerEntityBase> getExplorers() { return this.explorers; }
public void setExplorers(List<ExplorerEntityBase> explorers) { this.explorers = explorers; }
}
.
#Entity
public class MountainEntity extends MountainEntityBase implements Serializable {
public List<MountainEntity> allMountainsExploredBy = new ArrayList<MountainEntity>();
#Transient
public List<MountianEntity> getAllMountainsExploredBy(String explorerName){
// Implementation
}
}
So any extended class will define only #Transients in its body. But also I want to allow for situations where the child class is empty:
#Entity
public class MountainEntity extends MountainEntityBase implements Serializable {
}
Thanks in advance for any help with this.
Inheritance in JPA is specified on the root entity using the #Inheritance annotation. There you can specify the database representation of the hierarchy. Check the documentation for more details.
If your child classes define only transient fields (not methods) (i.e. not saved in the db), then perhaps a discriminator column is the best option. But it may be the case that you don't actually need inheritance - the main entity can have all the methods (because it has all the fields the methods operate on)

Categories