JAU: Java Annotation Based Utilities

I am glad to announce the first stable beta ;-) of the JAU library (http://code.google.com/p/jau/). Version 0.6 implements the following methods using annotations (and works for POJOs):

  • equals
  • hashCode
  • toString
  • compareTo
  • copy/clone
  • toMap/fromMap (this one creates a java.util.Map with object properties as entries)
 

Here is a sample use case:

import com.googlecode.jau.*;

@JAUEquals
@JAUHashCode
@JAUToString
@JAUCompare
@JAUCopy
@JAUToMap
public class UserData implements Comparable, Cloneable {
private String firstName;
private String secondName;
private Date birthDate;
private String login;

@JAUEquals // not really necessary. All fields are included by default
private String[] rights = new String[] {"view"};

@JAUEquals(include=false)
@JAUHashCode(include=false)
@JAUCompare(include=false)
private javax.swing.event.EventListenerList listeners = new javax.swing.event.EventListenerList();

// constructor omitted

public boolean equals(Object obj) {
return JAU.equals(this, obj);
}

public int hashCode() {
return JAU.hashCode(this);
}

public String toString() {
return JAU.toString(this);
}

public int compareTo(Object obj) {
return JAU.compare(this, obj);
}

public UserData clone() throws CloneNotSupportedException {
UserData r = (UserData) super.clone(this);
JAU.copy(this, r);
return r;
}
}

For example, the output for

JAU.toString(new String[][] {{"a", "b"}, {"1", "2"}})

would be

java.lang.String[][java.lang.String["a", "b"], java.lang.String["1", "2"]]
0
Average: 3 (2 votes)

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Ronald Miura replied on Wed, 2009/01/28 - 6:50am

Now this is a clear misuse of annotations...

And, what are the annotations supposed to do in your example, since you end up calling the JAU utility class anyway?

Arek Stryjski replied on Wed, 2009/01/28 - 7:06am

Ronald is right, using annotations to dynamically change a class is misuse from annotations specification point of view, but I don't have a problem with it. 

But I have many questions:

  • How it is done? Do I need to compile it in special way, or it is done at runtime?
  • What if I have already implementation any of this methods in my Class?
  • Is it based on any AOP framework?
  • Is it possible to extend it, so I can also add dynamically implementation of other methods?

In case you are not inserting the implementation:

  • What is a point of using annotations?
    (I apology for such a stupid question, I know nobody will put annotations just for code decoration.)

 

Tim replied on Wed, 2009/01/28 - 7:12am in response to: ronaldtm

Misuse!? The use case is very similar to EJB 3.

JAU.equals for example compares 2 objects and only use fields for comparison annotated with @JAUEquals. Other methods work in similar fashion.

Calling JAU.equals and similar methods is only necessary to avoid bytecode modification or code generation yet to override the Object.equals() method.

 

Tim replied on Wed, 2009/01/28 - 10:18am in response to: areks

It doesn't change the class dynamically. There is no bytecode modification.

  • It is done at runtime using reflection. You don't need to compile it in a special way. Java 5 is enough. Java 6 is necessary if you would like to annotate whole packages.
  • For example classes annotated with @JAUEquals are compared using reflection, for classes, which are not annotated,  .equals() method is used
  • Is is written in plain Java 5. No AOP.
  • Right now there are only implementations for
    @JAUEquals
    @JAUHashCode
    @JAUToString
    @JAUCompare
    @JAUCopy
    @JAUToMap


    The library could be extended to provide a SPI for implementing similar methods.
    I didn't think about it yet, maybe later.
  • The main point is to specify classes/fields that should be compared (in case of @JAUEquals) using reflection and classes that should be compared using .equals. For example comparing  java.lang.String using reflection is probably not what You would like to have. Another example would be to ignore listeners stored in a field while comparing object:

    @JAUEquals(include=false)
    private EventListenerList listeners;

aldobrucale replied on Wed, 2009/01/28 - 9:00am

If I understand correctly, annotating the fields lets one choose which fields should be included in the equals/hashCode/etc. methods?

Tim replied on Wed, 2009/01/28 - 10:13am in response to: aldobrucale

exactly :-)

jkilgrow replied on Wed, 2009/01/28 - 6:58pm

Just one simple question: "Why?"

joecoder replied on Wed, 2009/01/28 - 8:08pm

I understand what you're doing (after reading the comments). I think it would help if you showed a better example where the annotations were used to specify which fields should be processed in the various JAU functions.

aruggles replied on Wed, 2009/01/28 - 9:39pm

I thought the point of Annotating classes was to move the configuration from external files like XML to the code that it affects.  What this seems to do is move the code from one location to another.  I don't see the benefit/advantage.

Peter Thomas replied on Thu, 2009/01/29 - 1:04am

Misuse!? The use case is very similar to EJB 3

Heh. This kind of misuse was predicted three years ago in this blog post (not mine):

http://uri.jteam.nl/?p=15

I feel that this feature is being overly misused for all sort of purposes, without putting too much thought whether it is the right thing to do or not. It sort of reminds me of how XML was 4-5 years ago… soon someone will come up with a scripting language for annotations and we’ll all be very happy…

Tim replied on Thu, 2009/01/29 - 3:42am in response to: joecoder

to joecoder:

thanks. I have updated the code so it is more clear.

Tim replied on Thu, 2009/01/29 - 3:43am in response to: aruggles

to aruggles:

see the advantages in my reply to jkilgrow

Tim replied on Thu, 2009/01/29 - 3:49am in response to: pt93903

OK. I have read the article. And now I understand why You are talking about misuse. It is possible to define a class with only 10 fields and 77 annotations from different libraries/frameworks. Well, I tried to prevent confusion by prefixing the names of all annotation classes with @JAU. But I can't really improve the situation in such a case. A developer *must* understand the libraries he uses and consider the benefits/drawbacks of them.

Yardena replied on Thu, 2009/01/29 - 4:56am

Hi Tim, what about performance? I suspect this would run much slower than "normal" code doing the same.

Tim replied on Thu, 2009/01/29 - 5:16am in response to: yardena

You are right Yardena,

performance is an issue I want to address before 1.0 is ready. JAU will always be slower than hand-written methods (but You can write them in places where performance is important and use with JAU) because it uses reflection. On the other side nothing in the API prevents on-the-fly bytecode generation in the future versions. 

peterhuber replied on Thu, 2009/01/29 - 1:21pm in response to: yardena

I remember that there was a guy proposing a similar approach to equals and hashCode about half a year ago. His post led to heated discussion about performance of his solution which he had tested and had to admit was between very bad and horrible...I know that equals and hashCode play a big role in the world of java (though I again and again find "experienced" java programmers who simply don't care...until they get the bill) and as such I want to have them perform well in terms of speed. The annotated solution will only work well with byte-code instrumentation, otherwise it has to use reflection...even if cached it's bad because then I pay memory for speed.
And I really, really, really don't see why I should not use eclipse source-generate-equals function which generates me all I need as if taken out of the book 'effective java'? Yes, yes I have to do it whenever I want to add a field or remove one, but I like the 'do it myself and have the control' rather than the 'I guess it might work with that library'.

So probably someone will find the old thread with the guy that failed before...

 --

Found it myself: http://java.dzone.com/tips/refactor-safe-tostringbuilder

Tim replied on Thu, 2009/01/29 - 3:29pm in response to: peterhuber

to peterhuber:

thank you for the pointer /trying to be genuine was harder than it seemed.../.

You always trade something for something: memory for speed, usability for implementation time etc. JAU seems to trade ease of use/simplicity for performance and maybe flexibility.

I'll publish some numbers on the project site until 1.0 is released. What slowdown would You find acceptable?

BTW: the GridGain guy got 14/3 votes and me only 7/6. The times got tougher :-).  

 

peterhuber replied on Fri, 2009/01/30 - 3:04am

Tim - I really like the idea of making things easier. So I think I like your idea of making the tedious work of implementing equals/hashCode easier.

But you know that hibernate and other ORMs use these methods alot. They all have different levels of caches and do optimizations, for instance "don't write back an unchanged entity". Such an optimization relies on equals…If your convenient approach is only a small percentage slower than hand coded (with the help of eclipse) I would use the hand coded stuff for really 'big' projects (big in terms of page hits, say amazon).

If you solve the Problem of performance with byte code instrumentation than I guess I would build up some sort of fear like "what code is running there?". It's instrumented by hibernate, any arbitrary dependency injection framework like that with the season in its name...what was the name of this all-absorbing thingy...and last but not least with your stuff. Hopefully the debugger has any chance to resolve this mess if you have any problems.

As you might have guessed from my postings, I'm more the sceptic type of guy...just try to convince me/us with a sustainable solution.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.