Tuesday, October 12, 2010

Java Lambdas

I mentioned last month that I had found a way to add lambdas to the current JDK. I struck me that perhaps many of you had not used lambdas before, so I thought I make a small tutorial on one of the common uses:

Sql like queries against your objects.
Let’s start with the most basic,

Order By
If you where writing an Sql query to get people based on age, you would say

Select *
From People 
Order By age

or perhaps

Select *
From People a
Order By a.age

If you had an array of people in C# this would be written as 

Linq.OrderBy(people, a => a.GetAge())

Now, to do this in Java is a little more compacted, but very similar

Query.orderBy(people, new S1<Person>(a){{ret(a.getAge());}});

let’s look at some more examples.

Let’s say you wanted to get the names of all the people, you’d write a query like

Select a.names
From people a

Now, let’s translate that into java

Query.select(people, new F1<Person, String>(a){{ret(a.getName());}});

of course, with java you can select more than just primitives, if you wanted the Addresses, you could do that too.

 Query.select(people, new F1<Person, Address>(a){{ret(a.getAddress());}});

you can even do transforms

 List<Student> students = 
        new F1<Person, Student>(a){{ret(new Student(a.getName(), a.getAge()));}});

let’s move on.

Of course most queries wouldn’t be much use without the where statement. Let’s find the all the A+ students

Query.where(people, new F1<Person,Boolean>(a){{ret(a.getGradePoint() > 3.8);}})

btw: it might be worth expanding the code a bit. I’m cheating a little to help with understanding. For lambdas to work in Java, there is an unfortunate side effect. It gets call 1 extra time. Specifically the first time. So you need to pass in an object. That object  in the examples above is pre-defined as ‘a’ but in practice I usually use a blank object

new F1<Person,Boolean>(new Person()){{ret(a.getGradePoint() > 3.8);}}

unfortunately, you can’t use ‘null’ because it would throw a null pointer exception. I also use the null object pattern a lot: Person.Null
of course there are some standard calculations that are nice too.

what if you wanted the average length of a person’s name?
Query.average(people, new F1<Person, Number>(a){{ret(a.getName().length());}});

of course for averages, the return type F1<InputType, ReturnType>  must be a Number.

by now you probably see how easy it would be to get a minimum or maximum, so let’s up the lambda by using a local variable. Let’s find the person who got the closest grade to sally.

 final Person sally = getPersonByName("Sally");
 Query.min(people, new S1<Person>(a, sally){{
                    ret(Math.abs(a.getGradePoint() - sally.getGradePoint()));}});

You might have noticed the final key word, java requires that for locals to be used inside of the lambdas. you might also have noticed that this time I had to pass in 2 parameters (a, sally) to use sally inside you need to add her as a parameter as well. Lastly, I used the class S1<Input> here instead of F1<Input, ReturnType> this is just a convenience class which extends F1, since any where, min, or max queries require the output to be a  Comparable object, like Integer, String etc.

Well, I hope this got you interested in what lambdas can do for you. Check out the video and download the jar to get started at http://bit.ly/lambdas

Happy coding,
Llewellyn Falco

No comments: