Monday, July 25, 2016

Annotations, for better or for worse

A recent provocatively titled blog post bashing Java annotations kicked up an amusing amount of controversy. What I find notable about the post and its responses is that there doesn’t seem to be much agreement on annotations’ place in Java. My own take on this is that many popular usages of annotations are questionable, but we’re stuck with them.



Annotations started off innocently enough. They are simply a way to attach metadata to source code. I don’t think the folks who originally introduced annotations would have any idea that annotations would be so heavily (mis)used throughout the ecosystem. The Java Language Specification states that "An annotation is a marker which associates information with a program construct, but has no effect at run time." (9.7 Annotations)

No effect at run time, huh? But run time behavior seems to be the dominant use of Java annotations. A lot of Java code today are useless without the containers and frameworks that read their annotations and alter their behavior based on those annotations. Moreover, there are annotations that actually modify the class signature and bytecode, such as those from Project Lombok. If you use Project Lombok constructs such as @Getter, you end up with code that won’t even compile under vanilla Java. Talk about giving your code a Lombok-tomy.

Still, I think anti-annotations rants are ultimately futile. The reality is that there really is no alternative. We’ve tried XML, configuration files, various DSLs, vast class hierarchies, and nothing works better. I see that Gavin King, the inventor of Hibernate, is among those responding negatively to that post. And no wonder: pre-annotations Hibernate was an ugly mess of XML files. Nobody wants to go back there. Spring was originally XML-based but annotations seem to have won out. I know some people claim that convention over configuration (CoC) would work, but I think it’s no panacea. CoC carries a heavy cognitive load: you need to carry around that unwritten convention in your head all the time. There’s a practical complexity quota beyond which CoC frameworks simply run out of room.

Java has emphasized readability as it evolved over the years. That is, everything is explicit, even if it has to be written twice (where’s my type inferencing?). In his Computer article The Feel of Java, James Gosling describes Java as a “blue collar language”, “deterministic”, and “You feel like it’s going to do what you ask it to do”. I guess embedding annotations in code, rather than convention or fancy meta programming, is more in-your-face and more Java-like. And unless someone can demonstrate viable alternatives to, say, JPA persistence annotations, these annotations are not going away.

Related Links


Evil Annotations

3 comments:

  1. The idea that annotations are the best way to get metadata into your source code sounds quite ridiculous to me. Since metadata is nothing more than "data about data", and it is already possible to define data in your source code, why is it not possible to define data about data in the same source code?

    I do nothing but write enterprise applications which use forms on the front end and relational databases on the back end, and in my domain layer I have one class per database table. Each table has its own metadata, but within each class file I hold this metadata in a series of variables which are arrays such as the following:

    $fieldspecs - one entry for each column in the table which identifies its type, size and other attributes.
    $primary_key - identifies the column(s) which form the primary key.
    $unique_keys - identifies any candidates keys and their constituent columns.
    $child_relations - identifies all the tables which are related as children to this table.
    $parent_relations - identifies all the tables which are related as parents to this table.

    Originally I hard-coded the contents of these variables in the class constructor, but I quickly realised that when I wanted to change this metadata it would require changing the source code, so I looked for a better solution. My first step was to put all this metadata in a separate table structure file which could be loaded into the object by its constructor. My second step was to create a mechanism to create this file automatically instead of manually. I did this my building a small application called a Data Dictionary into which I can import the structure of each database table at the touch of a button. At the touch of another button I can export this data into a separate structure file for each table, overwriting any previous file. The eport process also create the table's class file, but this is never overwritten. This approach makes it very easy for me deal with database changes - I simply change the database structure then use my Data Dictionary to import then export the changed metadata. This means that I can deal with any database changes without having to change any source code.

    I don't use Java, only PHP, so can any Java programmers tell me why this simple approach is not used in that language? Surely it cannot be that what is simple in PHP is impossible in Java?

    ReplyDelete
    Replies
    1. Hi Tony. Thanks for sharing. I think the key to understanding Java annotations if you're coming from a non-Java world is that annotations is not strictly data about data. I know we call it "metadata" but what it really is is data about code. It's information about the code that it annotates, so it "belongs" with the code. That's not the same use case as the data dictionary example you gave.

      The reason executable code is not suitable for this information is that it is accessed before the code is executed. In fact, the code may never be executed. For example, an annotation can be used to generate documentation about a class method ("can this method return a null?"), or to check the code for errors ("is this method being used in a null-safe manner?"). In neither case is the code ever executed.

      The tricky part I was addressing in this blog post is where annotations is used to affect runtime behavior of the code. This is really going into a grey area, because that was not the stated purpose of annotations. Nevertheless, if you remember that Java annotations were originally supposed to convey information -- not behavior --
      about the code, maybe you can sort of understand why they are a separate construct from executable code.

      Delete
  2. This sounds like a typical case of scope creep - annotations were originally designed as nothing more than comments, but then some dingbat decided to stretch its usage into something else, and this started an avalanche which has resulted in a train wreck. The idea that comments should affect run-time behaviour is very, very bad and something that I will never include in my code.

    ReplyDelete