Please suggest how to use #Wither/#With when inheritance applied.
I have an abstract class Parent and concrete Child. Child is supposed to be immutable. Putting #Wither on both gives me two errors:
The constructor Child(String) is undefined
The type Child must implement the inherited abstract method
Parent.withA(String)
#Value
#Wither
#NonFinal
#SuperBuilder
abstract class Parent {
String a;
}
#Value
#Wither
#EqualsAndHashCode(callSuper = true)
#SuperBuilder
class Child extends Parent {
String b;
}
I'd be happy to just remove #Wither and use the builder methods, but I'm refactoring a public library (trying to optimize the model classes) and I don't want compilation errors on my clients.
I also found this issue that explains the second error. But the logic of the intention is not clear https://github.com/rzwitserloot/lombok/issues/945
Lombok is an annotation processor. Those run on each compilation unit (i.e. Java file) and do not have access to information from other compilation units. That means that Lombok cannot know anything about the contents of class Parent when processing Child.
So when generating the code for Child, Lombok does not know what wither methods are inherited from Parent. Consequently, it cannot generate an implementation for the abstract withA() from Parent.
A second problem is that the wither methods need a constructor that has all fields as parameter, including those from the superclass. That is also impossible to generate for Lombok due to the aforementioned limitation.
To make a long story short: #Wither does not work well with inheritance. I suggest putting it only on Parent and implementing it manually for Child.
Another option is to put #SuperBuilder(toBuilder=true) on both classes and then use instance.toBuilder().a("newValue").build().
Related
In many sites, I saw lombok #Builder can be used to create immutable objects here(https://www.baeldung.com/lombok-builder-singular) and also many sites say Builder pattern is mainly used to create immutable objects.
TimeIntervalData td = TimeIntervalData.builder().endTime("12:00").startTime("10:00").build();
td.setEndTime("14:00");
System.out.println(td.getEndTime());
I am not sure how I can use setters on objects built using builders. Is there something I am missing here?
Yes lombok builder will not create immutable instances util user define the parameters in class as final, As per the docs from lombok.builder #Builder lets you automatically produce the code required to have your class be instantiable with code such as:
Person.builder().name("Adam Savage").city("San Francisco").job("Mythbusters").job("Unchained Reaction").build();
As per the docs it just creates inner static class with the same properties in the same way that mentioned in outer class
A method annotated with #Builder (from now on called the target) causes the following 7 things to be generated:
An inner static class named FooBuilder, with the same type arguments as the static method (called the builder).
In the builder: One private non-static non-final field for each parameter of the target.
In the builder: A package private no-args empty constructor.
In the builder: A 'setter'-like method for each parameter of the target: It has the same type as that parameter and the same name. It returns the builder itself, so that the setter calls can be chained
In the builder: A build() method which calls the method, passing in each field. It returns the same type that the target returns
In the builder: A sensible toString() implementation.
In the class containing the target: A builder() method, which creates a new instance of the builder.
But using #Singular with #Builder annotation for collection properties makes them singleton
By annotating one of the parameters (if annotating a method or constructor with #Builder) or fields (if annotating a class with #Builder) with the #Singular annotation, lombok will treat that builder node as a collection, and it generates 2 'adder' methods instead of a 'setter' method. One which adds a single element to the collection, and one which adds all elements of another collection to the collection. No setter to just set the collection (replacing whatever was already added) will be generated.
#Singular can only be applied to collection types known to lombok.
is mainly used to create immutable objects
This statement is not correct. The main purpose is to reduce boiler plate code. Creation of object using builder is compacter, it is easier to write and is easier to read.
When you have 1-2 attributes, builder can be disadvantage and can make the code less readable compared to passing parameters via constructor.
So when to use or not to use builder is a matter of taste.
To setters: If you need setters, you can generate them by #Data annotation.
In sonar rules, there is a S00107 rule for "Methods should not have too many parameters". This rule has exceptions for some annotations:
Exceptions
Methods annotated with Spring's #RequestMapping (and
related shortcut annotations, like #GetRequest) or #JsonCreator may
have a lot of parameters, encapsulation being possible. Such methods
are therefore ignored.
Is it possible to add another annotations to this exceptions?
My case: I have constructors, annotated by the Lombok's #Builder, which contains a lot of parameters! So i want to ignore this rule on these constructors.
public class MyClass extends MySupperClass {
#Builder
public MyClass(String a, int b, ..., String z) {
}
}
Try to add #SuppressWarnings("squid:S00107")
Move #Builder to class level,
If you need super fields use lombok's #SuperBuilder in class level
The #SuperBuilder annotation produces complex builder APIs for your classes. In contrast to #Builder, #SuperBuilder also works with fields from superclasses
Intellij IDEA's lombok-plugin version 0.27 added support
Fixed #513: Add support for #SuperBuilder
#Data
public abstract class B {
private final String str;
}
#Data
public class A extends B{
private final String s;
}
Data on class A complains in intellij, but the codes can get compiled successfully through command line, not sure what to do
One problem is that #Data is meant for mutable data and there's nothing mutable in your classes. So using #Data is simply wrong... and whether it compiles or not doesn't really matter.
If you want mutable data, then remove the final field. For immutable data, make all fields final and use #Value. Sometimes, partially mutable data is needed, but I try hard to avoid it as it's confusing (some fields can be set, some can't) and they provide disadvantages of both.
The other problem is that Lombok can't access class hierarchies. With B having a final field, you need it to be initialized in the constructor, which means that A's constructor has to call a non-default constructor. This isn't possible with Lombok. There's #Superbuilder in Lombok, which is about the only feature of Lombok dealing well with class hierarchies.
The #Data annotation does not add a default contructor.
Try to add a #NoArgsConstructor to your Base Class B to generate a default constructor with Lombok.
You can also read up what #Data actually means here.
This does not event compile. In Intellij, when you are not sure what is the problem with lombok code, you can open class in which you are unsure, go on Refactor -> Delombok -> All lombok annotations and you will see what lombok actually create for you.
I am storing some properties in lombok.config, which, I though, is a great way to reduce unnecessary stacks of lombok annotations on my classes. One of these properties is the following;
lombok.equalsAndHashCode.callSuper = CALL
I though this would shut the compiler up & end the following IDE (Intellij) warnings about putting an #EqualsAndHashCode with callSuper value set on those classes with #Data and extending some other class;
Generating equals/hashCode implementation but without a call to superclass, even though this class does not extend java.lang.Object. If this is intentional, add #EqualsAndHashCode(callSuper=false) to your type.
It does not though! Is there a way to stop these warnings without adding the callSuper property directly on the classes?
I have a bunch of classes, which in turn contain many inner/nested classes, that need annotations for deserialization (#JsonIgnoreProperties) and for reducing Equals overriding boilerplate (#EqualsAndHashCode, Groovy default API, not Lombok).
My code looks like this:
#JsonIgnoreProperties(ignoreUnknown = true)
#EqualsAndHashCode(includeFields = true)
class SomeClass {
String id
#JsonIgnoreProperties(ignoreUnknown = true)
#EqualsAndHashCode(includeFields = true)
class InnerOne {
String foo
}
#JsonIgnoreProperties(ignoreUnknown = true)
#EqualsAndHashCode(includeFields = true)
class InnerTwo {
String bar
}
// and so on...
}
Both those annotations belong to third party libraries and are not marked as #Inherited, hence I'm forced to repeat them in each inner class.
Is there a way to reduce that boilerplate?
The author of the annotation processor has decided whether it is a good idea to inherit annotations or not.
The author might write the annotation processor to inherit annotations, so that the user has to write fewer annotations.
The author might write the annotation processor to not inherit annotations, so that the meaning of a class is obvious by looking at the class alone, without having to read every superclass as well.
Either of these is a defensible choice. I tend to lean toward the latter, because it improves readability, and code is read more often than it is written.
If you wish to change the behavior, you will need to modify the semantics of the annotation processor by modifying its source code. In your case, it is probably easier to write the extra annotations.