Google collections and enhanced JavaBeans
I’m a big fan of Google Collections. Functions and Predicates became my best friends to make Java’s syntax a bit functional-like. I like writing things like:
List<Contact> contacts = ...
List<String> toStrings = Lists.transform(contacts, Functions.toStringFunction());
and with the help of static imports:
List<String> toStrings = transform(contacts, toStringFunction());
If you use out-of-the-box Functions and Predicates, it’s a lot of fun. Now, let’s say, you want something more useful like:
List<String> names = transform(contacts, Contact.T0_NAME);
you need to accommodate with Java’s way of writing functions. That is write an inner class:
public class Contact {
...
public static Function<Contact, String> TO_NAME = new Function<Contact, String> {
public String apply(Contact contact) {
return contact.getName();
}
}
It’s ok for one bean property, but writing functions for all of them is a lot of work.
What if we could write a JavaBean that would generate all these functions for us. Something like that:
public class Contact extends AbstractBean<Contact> {
public static final StringProperty<Contact> NAME = stringValue();
public static final IntegerProperty<Contact> AGE = integerValue();
public static final StringProperty<Contact> EMAIL = stringValue();
public Contact(String name, int age, String email) {
super(NAME, name, AGE, age, EMAIL, email);
}
public String getName() {
return get(NAME);
}
public Integer getAge() {
return get(AGE);
}
public String getEmail() {
return get(EMAIL);
}
}
Now, every property of my bean is also a Function converting a bean instance to it’s property value. To be clear:
Contact.NAME is a Function<Contact, String>
Contact.AGE is a Function<Contact, Integer>
Contact.EMAIL is a Function<Contact, String>
so that I can write:
List<Contact> contacts = ...
List<String> names = transform(contacts, Contact.NAME);
List<Integer> ages = transform(contacts, Contact.AGE);
Nice isn’t it?
Now, it can be even cooler. I can write something like that:
Iterable<Contact> david = filter(contacts, NAME.isEqualTo("david"));
... adults = filter(contacts, AGE.isGreaterThan(18));
... babies = filter(contacts, NAME.isEqualTo("baby").and(AGE.isLessThan(1)));
My bean properties are also Predicate factories!
With a custom utility class, I can even use a clearer syntax that Google Collection’s:
... ages = with(contacts).keep(NAME.isEqualTo("david")).to(AGE).list();
There are even more features. All AbstractBeans come with free equals(), hashCode() and compare().
The last neat feature is the ability to write a builder for any bean easily, with only one additional method:
public class Contact extends AbstractBean<Contact> {
...
static <V> BeanBuilder<Contact> with(BeanProperty<Contact, V> property, V value) {
return new BeanBuilder<Contact>() {
public Contact createBean(PropertyValues<Contact> values) {
return new Contact(values.get(Contact.NAME), values.get(Contact.AGE));
}
}.with(property, value);
}
Now, I can write something like this:
Contact contact = with(NAME, "david").with(AGE, 34).with(EMAIL, "d@g.net").build();
Note, that all of the features are statically typed and don’t use reflection.
What do you think?
J’aime bien!! Exactement le genre de choses qui me passe en tête quand pour la nieme fois il faut écrire un foreach/if/add… Mais ça garde quand même l’élégance pachydermique du java
Très élégant !
Peux-tu nous mettre un lien vers ton fichier java complet pour avoir la “big picture” ?
Dans le même esprit, en JavaScript, l’API LivePipe propose un module Event.Behavior:
http://livepipe.net/extra/event_behavior
@Jean-Philippe Encausse C’est du code expérimental illisible mais je l’ai quand même mi ici: http://pastie.org/679917
Have you tried out LambdaJ (http://code.google.com/p/lambdaj) yet? It seems to be capable of what you wish for without language extensions.
@F. Degenaar Thanks, lambdaj looks very nice. Will give it a try.
Looks really nice, I’d love to host a tutorial on my web 2.0 site if you’d ever be interested
Where is the source code for AbstactBean?