10 Aug 2019
Java is quite a verbose language and annotations can solve almost any problem.
Right? - What a dangerous combination that is.
I understand it, totally. I’m lazy too.
But other than saving all that boilerplate, have you considered the repercussions?
Magic is harmful
I don’t want any magic in my code.
It is the thing that makes behaviour opaque.
It makes you fire up your debugger, because you don’t actually understand what is going on behind the scenes.
There should be no behind the scenes.
Everything should be as explicit as possible, for economic reasons.
Magic is a feature with non-compositional semantics that succeeds in making the common case easy, at the cost of making the uncommon cases surprising, impossible, or ridiculously complex.
~ John A De Goes
@Data, @Getter and @Setter are a design smell
When I design the objects of my domain I want to avoid accessors.
I want those objects to represent a set of behaviours on nicely encapsulated data.
I’ll occasionally have some public fields on my Value Objects, but that’s about it.
Why would I want getters or setters on these objects? They are a backdoor to the data that ought to be modified through specific methods.
This smells like Feature Envy and Anemic Domain Model.
Also, setters work against my intention of making most of my objects immutable.
After all, immutable objects are easier to refactor.
So where do I need accessors then?
Maybe on my data transfer objects?
Why would I want to go the length of making fields private and creating accessors on a structure that is solely data?
Does this kind of information hiding provide any value? To me, it is just clutter.
public final
fields without accessors are a much cleaner solution here imho.
And there I am, left without a need for accessors.
@AllArgsConstructor introduces connascence of position
This awful annotation couples the order of your fields to the order of its generated constructor arguments.
So when you reorder your fields, you effectively break your code.
That’s an awful connascence of position right there.
Bi-directional object relations cause stack overflows
So when you use Lomboks equals, hashCode or toString implementations, it is interresting that bi-directional associations will cause a stack overflow.
I’m not saying bi-directional associations are desirable, or that those stack overflows aren’t obvious.
All I am saying is, that if Lombok was just a little bit smarter, it could probably have avoided those.
The bad thing is not the stack overflow. It’s the fact that it is hidden behind an annotation.
The Lombok plugin might hit your cpu
If you do use lombok annotations excessively and have a lot of pojos in your code base, the lombok plugin might hit your cpu.
I had one project where it caused the autocomplete drop down to load several seconds in specific areas of the code.
The problem was gone once I deactivated the plugin.
To be fair: Maybe it was just a temporary problem with the plugin that has already been addressed.
Compatibility issues with Java versions greater than 8
When I tried migrating a java 1.8 project that used lombok to java 11, I ran into issues.
And despite the known issues and fixes that I found on the internet, I was not able to get it to work again within 2 hours of trying.
Delombok to the rescue.
update: seems like i ran into a similar issue like this, trying to upgrade to java 11.
Without lombok, what are the alternatives?
We can just fall back to explicit pojo code. Some libs, ide features and plugins will help us out with the boilerplate.
Also, we may use Java records starting with Java 14.
equals and hashcode
For value objects, where you want all fields included, the apache commons lang EqualsBuilder and HashCodeBuilder come in handy.
@Override
public boolean equals(Object that) {
return EqualsBuilder.reflectionEquals(this, that);
}
@Override
public int hashCode() {
return HashCodeBuilder.reflectionHashCode(this);
}
maybe even use an intellij live template like this:
@Override
public boolean equals(Object that) {
return org.apache.commons.lang3.builder.EqualsBuilder.reflectionEquals(this, that);
}
@Override
public int hashCode() {
return org.apache.commons.lang3.builder.HashCodeBuilder.reflectionHashCode(this);
}
@Slf4j
I recommend using an intellij live template again.
// just put in 'className()' for the $CLASS$ variable
private static final org.slf4j.Logger LOG = org.slf4j.LoggerFactory.getLogger($CLASS$.class);

@Builder
I really like the builder generation plugins that are freely available on the intellij plugin market.
Also, there is a new Replace Constructor with Builder refactoring available now.
However, I prefer the Inner Builder plugin.
A case for Delombok
Thankfully, there is this nice little feature called delombok.
It will transform all your former lombok annotations into the ugly boilerplate they created behind the scenes,
so you can completely get rid of it.
Don’t expect a beautiful outcome though, the generated code will contain a lot of @SuppressWarnings("all")
.
Also, the delombok feature of my intellij plugin caused my ide to freeze.
So I had to use the Lombok Maven Plugin instead, it worked just fine.
23 Jul 2019
Just recently i was refactoring some very messy legacy java code and i stumbled upon this really long and complex method.
I had to refactor it but there were no tests, no safety net, what so ever.
It had so many different paths that adding unit tests would have been a tedious endeavour.
“Would be nice if had something like suture”, i thought.
So i decided to just code it real quick.
Here is the result: https://github.com/gregorriegler/seamer.
Seamer is a refactoring tool for java that helps you get complex methods under test in order to make safe refactorings.
How does it work?
Basically you just wrap a complex method with a lambda and the seamer will then record all your invocations including
input arguments and return values, serialize and persist them.
You can later replay those invocations to make sure everything still works.
Seamer provides an api for you to make suggestions on the arguments you want to pass in.
The tool will shuffle your suggestions and execute given arguments in all possible combinations.
Or you can just let it run, click around in your application, and record real values.
When you are confident with the data you have recorded, go ahead and start your refactorings.
Replay the seamer regularly to make sure everything still works.
If you have ideas for further improvements, feel free to do so, or just make a pull request.
30 Jun 2019
i was doing a little bit of tdd in typescript recently and the setup always took me way too long.
i was trying to do things right. i didn’t want any magic in my setup, everything i added should have a purpose.
i wanted it to be simplistic but working. this is my outcome.
https://github.com/gregorriegler/tdd-setup-typescript-mocha-chai
feel free to use, or improve it.
20 Dec 2013
@XmlElementWrapper(name="orders")
@XmlJavaTypeAdapter(OrderJaxbAdapter.class)
@XmlElements({
@XmlElement(name="order_2",type=Order2.class),
@XmlElement(name="old_order",type=OldOrder.class)
})
@JsonIgnore
@JsonProperty
@NotNull
@ManyToMany
@Fetch(FetchMode.SUBSELECT)
@JoinTable(
name = "customer_order",
joinColumns = {
@JoinColumn(name = "customer_id", referencedColumnName = "id")
},
inverseJoinColumns = {
@JoinColumn(name = "order_id", referencedColumnName = "id")
}
)
private List orders;
Wait. What? Is this really what we have come to? I can't even see the damn property under this bloat. How did this happen? Yeah ok - we had to get rid of the old xml configuration horror somehow. But this? This is even WORSE. This class is supposed to be a goddamn pojo with a bunch of properties. Short and concise, easy to read. I, as a reader of this cass, am not interested at all how the database table is joining customers to orders. I'm neither interested how its being serialized. This is just implementation details. Reading this class, i am living in an object world and i want to know what data and behaviour the object has. Not more, not less. I don't care about column names, fetchtypes or json serialization for the moment. And i don't want to read, change or recompile this class for the sake of a tablename change. I don't want to add another annotation for storing this entity in a mongoDB neither. The entity should not have responsibility for these details. We are not only violating the Single Responsibility Principle here, we are doing a f****** responsibility party.
Ok ok, enough of the rage. How do we deal with this issue? Some duplicate the entity for various layers with different annotation purposes. They map the entity onto the next layers related entity using an automated mapper like Dozer. Some even write that mapping themselves. But this is by no means a solution. It is just replacing one code smell with another one: Duplication.
So please, focus on frameworks that don't force you to clutter your code. jOOQ is a nice solution to map database records to entities without annotations. Also, hibernate allows you to define your mappings in XML.
Private Field Injection
@Inject
private MyService myService
This is used quite often, while it shouldn't even be possible. The myService field is private, thus it's inaccessible from outside the class. Nevertheless it's possible and people do it. In reality it is a hack. The DI-framework sets the field using reflections doing setAccessible(true). You don't want hacks in your code, do you? Lets have a look at the alternatives:
Setter Injection
Well, at least its better than the private field injection, since its using a public method instead of hacking a private field. But still, ask yourself this: 'Is this class even supposed to live without the injected value?' Because if it's not, there is no reason what so ever for the class to get constructed without an instance of MyService. You want to implement this constraint on the class level and inside the constructor, not on the framework level.
Constructor Injection
This is usually the way to go. It allows you to
- make the field immutable (there is usually no need to change it).
- implement the constraint, that the class is not instantiatable without a given MyService in the right place.
Of course it means that you cannot inject by annotation. But why would you want to? The class doesn't need to know, if it gets injections by a DI-Container or a Factory Class. It should not know anything of this. No @Autowired, no @Qualifier. All it needs to know is its own behaviour. Everything else should be handled outside of the class.
One could use a configuration class or file for the actual injection.
A DI-Container is a usefull tool that helps you wire your classes together. Use it for this purpose, but don't let it dictate your code. Uncle Bob wrote a great post where he explained how to use DI-Frameworks without having them dictate your code.
@RunWith(SpringJUnit4ClassRunner.class) in UnitTests
Why would you need this in unittests? Because it is automatically generated by your IDE/app template? No! You want to test the behaviour of a class, living in isolation in unittests. Not if the DI-Conainer is injecting a fields accordingly. Just inject yourself in a setup method. No DI-Container needed. By the way, all this testrunner does is these 3 lines of code.
private TestContextManager testContextManager;
//..
this.testContextManager = new TestContextManager(getClass());
this.testContextManager.prepareTestInstance(this);
They are not worth blocking your only TestRunner slot. You want to keep it free for something like parameterized @RunWith(JUnitParamsRunner.class) or concurrency @RunWith(ConcurrentJunitRunner.class) tests.
tl;dr
Annotations have become more harmful than helpful these days. We should get back to pojos and focus on keeping our code as clutterless and framework-agnostic as possible to make it more readable and reuseable. Don't let frameworks dictate your codebases, since they should be exchangable tools. Beware of what a class should know, and what not. Some annotations are useful, most aren't.
@DevNull({
@SuppressWarnings
@Autowired,
@Inject,
@Override,
@XmlElementWrapper,
@XmlJavaTypeAdapter,
@XmlElement,
@JsonIgnore,
@JsonProperty,
@ManyToMany,
@Fetch,
@JoinTable
})