Project Lombok - Value annotation: is it possible to suppress #Getter? - java

After scouring the Lombok feature-list and in particular the documentation for the Getter/Setter and #Value annotations I have not been able to find any setting that suppresses the code generated by #Getter.
In practice, #Value is shorthand for: final #ToString #EqualsAndHashCode #AllArgsConstructor #FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE) #Getter
This is important as I do not want to leak references to objects that are themselves mutable.
Effective Java references this type of issue in "Item 39: Make defensive copies when needed". It seems that #Wither could partly solve this issue by making actual defensive copies but I want to avoid leaking attributes what so ever, regardless of them being mutable.
While it is possible to roll one's own #Value annotation that omits the #Getter I would, of course, prefer not to as that would add unwarranted complexity to the codebase if such a setting already exists.

You could use:
#Value
#Getter(AccessLevel.NONE)
AccessLevel.NONE instructs Lombok to not generate the getters. That's the best you can do right now.

Related

Different effect when using plural method on Collection field with and without #Singular annotation

I want to have immutable myClass objects. Good solution seams to be using #Singular annotation.
The problem is when I use this annotation the method elements() appends elements to existing list, instead of creating the new one:
Let's assume that that we have:
#Value
#Builder(toBuilder = true)
public class MyClass {
#Singular
private List<String> elemets;
}
and
MyClass.builder()
.elemets(Arrays.asList("elem1"))
.elemets(Arrays.asList("elem2"))
.build();
Without #Singular annotation we have elem2 on the list
with #Singular annotation we have both elem1 and elem2, if I want to have elem2 only I have to use clearElements() before.
Why implementation is different? Is it possible to use #Singular with my own implementation?
With #Singular annotation I cannot implement elemets(List elemets) method in MyClassBuilder class because I get: "Manually adding a method that #Singular #Builder would generate is not supported. If you want to manually manage the builder aspect for this field/parameter, don't use #Singular."
First let me say that using #Singular isn't necessarily the best solution -- it depends on your use case.
However, in most cases where you want to ensure immutability on classes that use collections, it is a good choice.
#Singular behaves the way it does because the Lombok designers decided that it's a good choice to do so. And I agree: It makes the two setter methods behave similarly; and in those rare cases where you want to reset the elements in a builder, you have the clear method.
#Singular generates pretty complex code (see https://projectlombok.org/features/BuilderSingular for an example). This is to ensure properties like efficiency and immutability (also when reusing builders to produce several objects). When you mess around with that code, you can easily violate these properties. Lombok prevents you from doing that.
If you really want to modify this aspect, you have three choices:
delombok the builder code, copy it into your class, and modify it.
Add another differently named method, like clearAndSetElements(List<String>). But that's probably even more confusing.
Remove #Singular and implement the setter methods on your own. You will have to put some effort in the implementation if you want the properties of Lombok's implementation; you can use the delomboked code as inspiration.

how to generate constructor in a enum using Lombok

I want to use lombok in a enum, but I can't find the annotation to generate the constructor. I checked the Lombok manual that it shows there should be a annotion named #XArgsConstructor,but I can't find it, any advice ? thanks.
X in #XArgsConstructor is just a placeholder for No, Required or All.
The real annotations are #NoArgsConstructor, #RequiredArgsConstructor and #AllArgsConstructor. Pick one of these (I'd go for #AllArgsConstructor by default), and it'll work.

lombok.config property is not stopping compiler/IDE warning

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?

lombok builder creating issue with hibernate list

I have a class lets say A. It has few non-null parameters. I am using lombok #NonNull and lombok #Builder. From documentation seems like Builder generates the not null check for parameters.
The other side of story is while retrieving A from DB using hibernate list it is throwing exception saying that A is missing no-arg constructor.
I have no idea on this. I have not defined any constructor for A except for #builder and #NonNUll checks for few parameters. Any idea whats going wrong?
I found solution. Though it seems like a work around.
Adding #NoArgsConstructor #AllArgsConstructor and #builder seems good. Hibernate and builder both can happily work toegther with this
Add the #NoArgsContructor to your class.
Hibernate uses this constructor and then sets the properties.
When you're using #Builder, i think Lombok is generating a constructor that is not the no args one.

Too much boilerplate, how can I reduce my POJO builders?

I have several different POJOs that use a builder pattern, but after adding a builder for each one and generating Object.toString, Object.hashCode and Object.equals, my classes end up being around 100 lines of code. There has to be a better way to handle this. I think having some sort of a reflective builder would help out a lot, but I'm not sure this would be good practice and I'm also not sure how I'd exactly make it happen. In other words, is there a way to implement a builder like this?
A simple POJO:
public class Foo {
public int id;
public String title;
public boolean change;
...
}
Then some sort of reflective builder:
Foo = ReflectiveBuilder.from(Foo.class).id(1).title("title").change(false).build();
Short answer no. What you ask for is not possible. Reflection looks at the code at runtime and invokes methods dynamically, it cannot generate actual methods.
What you could do would be:
Foo foo = ReflectiveBuilder.from(Foo.class).
set("id", 1).
set("title", "title").
build();
This has three massive problems:
the fields are Strings - a typo causes a runtime error rather than a compile time one,
the values are Objects - the wrong type causes a runtime error rather than a compile time one, and
it would be much slower than the alternative as Reflection is very slow.
So a reflection based solution, whilst possible (see Apache Commons BeanUtils BeanMap) is not at all practical.
Long answer, if you're willing to allow some compile time magic, you can use Project Lombok. The idea behind Lombok is to generate boilerplate code from annotations using the Java annotation preprocessor system.
The really magical thing is that all IDEs, well the big 3 at least, understand annotation preprocessing and code completion will still function correctly even though the code doesn't really exist.
In the case of a POJO with a Builder you can use #Data and #Builder
#Data
#Builder
public class Foo {
public int id;
public String title;
public boolean change;
...
}
The #Data annotation will generate:
a required arguments constructor (that takes all final fields),
equals and hashCode methods that use all fields (can be configured with the #EqualsAndHashCode annotation)
a toString method on all fields (can be configured with the #ToString annotation and
public getters and setters for all fields (can be configured using the #Getter / #Setter annotations on fields).
The #Builder annotation will generate an inner class called Builder that can be instantiated using Foo.builder().
Do make sure you configure the equals, hashCode and toString methods as if you have two classes with Lombok that have references to each other then you will end up with an infinite loop in the default case as both classes include the other in these methods.
There is also a new configuration system that allows you to use, for example, fluent setters so you can more of less do away with the builder if your POJO is mutable:
new Foo().setId(3).setTitle("title)...
For another approach you can look at Aspect-oriented programming (AOP) and AspectJ. AOP allows you do chop your classes up into "aspects" and then stick them together using certain rules using a pre-compiler. For example you could implement exactly what Lombok does, using custom annotations and an aspect. This is a fairly advanced topic however, and might well be overkill.
Maybe Project Lombok (yes the website is ugly) is an option for you. Lombok injects code into your classes based on annotations.
With Lombok you use the #Data annotations to generated getters, setters, toString(), hashCode() and equals():
#Data
public class Foo {
public int id;
public String title;
public boolean change;
}
Have a look at the example on the #Data documentation section to see the generated code.
Lombok also provides a #Builder that generates a builder for your class. But be aware that this is an experimental feature:
#Builder
public class Foo {
public int id;
public String title;
public boolean change;
}
Now you can do:
Foo foo = Foo.builder()
.id(123)
.title("some title")
.change(true)
.build();
I personally use this website to create all the boilerplate code for the POJOs for me. All you need to do is to paste the JSON that you want to parse, and it will generate all the classes for you. Then I just use Retrofit to do the requests/caching/parsing of the information. Here is an example of Retrofit and POJOs in my Github account.
I hope it helps!
I created a small library CakeMold to do fluent initialization of POJOs. It uses reflection, what is certainly not fast. But can be very helpful when need to write tests.
Person person = CakeMold.of(Person.class)
.set("firstName", "Bob")
.set("lastName", "SquarePants")
.set("email", "sponge.bob#bikinibottom.io")
.set("age", 22)
.cook();

Categories

Resources