Skip to Content

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?

comments powered by Disqus