(Groovy/Java) Inheriting annotations to inner classes - java

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.

Related

Can lombok generate builder based on the type solely?

I have a use case where I don't want to use the #Builder on the class itself, so I created method based builder like this:
#Builder(builderMethodName = "carBuilder")
public static Car build(int speed, String brand){
Car car = new Car();
car.setSpeed(speed);
car.setBrand(brand);
return car;
}
But how can I handle when the given class has ton of fields (over ~20).
Should I really specify them as parameters and invoke the setters by hand?
Couldn't just lombok generate them automatically based on the type?
Currently this is not possible because Lombok avoids inspecting types from elsewhere on the classpath when processing a file.
What's your reason for not adding #Builder to the class itself? If you can describe a common use case for that, you or someone else might be able to add this ability to Lombok. However, currently I can't see any good reasons for this. Most libraries should be relatively easy to use already and if its your own code, why not just add Lombok?
Also the main reason I add #Builder is because I want my classes to be immutable - given that the actual object is still mutable here, why use builder rather than the setters?

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.

Lombok #Wither/#With Inheritance (super-/sub-classes)

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().

Does this code violate open-closed principle?

I want to know if the below code is violating open closed principle.
Animal is a parent class of Dog, however Animal has jackson annotations that help ObjectMapper (de)serialize the classes. Anyone who extends Animal will have to edit only annotations present on Animal to make sure (de)serialization works as intended leaving the class untouched.
#JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
property = "type")
#JsonSubTypes({
// all subclasses
#Type(value = Dog.class, name = "dog")
})
public abstract class Animal {
// fields, constructors, getters and setters
}
public class Dog extends Animal {
}
Indeed it does. The idea of the open-close principle is to make objects extendable without having to modify them internally. Since any new child of Animal would have to modify it to work properly, it breaks the principle.
Theoretical point of view
Open/closed principle like the whole SOLID is Utopia. We should continually upgrade our code in that direction but probably we will never end up there because this is not possible. Let's read below articles to see how classical getters and annotation constructs can be debatable.
Printers Instead of Getters
Java Annotations Are a Big Mistake
Practical point of view
Like every practical programmer I like to use good tools to solve problems instead of implementing something new myself. When I am asked to serialise given model to JSON files I am checking whether it is:
Open-source
Fast
Under active development
It is easy to use
When we are talking about Jackson and it's annotations, I think, we can find golden middle way between theory and practice. And this is thanks to MixIn feature. You can separate model from the way how it is serialised to JSON. Of course when you add new class which extends base class you need to change MixIn interface with annotations but this is a price we need to pay.
Edit or Why I forgot to answer a question?
Sorry, I forgot to answer a question whether above example violates Open/Closed principle or not. First, get definition from Wikipedia article:
A class is closed, since it may be compiled, stored in a library,
baselined, and used by client classes. But it is also open, since any
new class may use it as parent, adding new features. When a descendant
class is defined, there is no need to change the original or to
disturb its clients.
Above example violates When a descendant class is defined, there is no need to change the original part. Even if we use MixIn there is a need to change other part of app. Even more, if your solution uses annotations in 99.99% of cases you violate this part because there is a need to configure somehow functionality which is hidden behind them.
Open/closed means a class should be open for extension, but closed for modification.
In other words... if you want to change the behavior of a class you should extend it in some way, but you should not modify it.
You can extent a class by
creating a subclass. This is usually done using e.g. the template method pattern.
defining an interface that class A uses so that it's behavior can be extended by passing it another instance of that interface, e.g. a strategy pattern. A good real life example is a TreeSet(Comparator<? super E> comparator), because it's sorting behavior can be changed without modifying TreeSet itself.
From my point of view the #JsonSubTypes annotation is not part of the behavior of the Animal class. It changes the behavior of another class - the object mapper. Thus it is not really a violation. Not really means that even if you don't change the behavior, you have to touch the Animal class and recompile it.
It is a really weird design of the annotation. Why did that json developers not allow you to put an annotation on the subclass, e.g. like JPA does when it comes to hierarchy mapping. See DiscriminatorValue
It is a strange design that a supertype references subtypes.
Abstract types should not depend on concrete ones. In my opinion that is a principle that should always be applied.

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