Sunday, August 8, 2010

Hacking closure in Java 7.0 (2): Capturing local variable and instance

I spent some times yesterday and today to play around with the closure prototype right from the source. First, I reinstalled the binary jdk 7 to the build 103 one and then get the langtools project from mercury. Finally, I built the project and got my javac that can compile closure. 


We have seen in 007 - Hacking closure in Java 7.0 (1): SAM Conversion how a closure can be written. Now, let's see more in detail some interesting properties owned by a closure.

Let's start with an example of a class FareList below that has a list of fares as follow:


public class FareList {
    private List<Double> listOfFares = new LinkedList<>();
    public FareList() {
    }
    public FareList(List<Double> fares) {
        listOfFares.addAll(fares);
    }
    public List<Double> getFares() {
        return listOfFares;
    }
...
}




Now, imagine that we have a discount( ) method that returns a new instance of FareList given a discount rate with the following signature:



    public FareList discount(double discountRate);



Just reminder, map is a ListUtil method that is introduced in 007 - Hacking closure in Java 7.0 (1): SAM Conversion and  has the following signature      
     
    public static <T> List<T> map(List<T> list, UnaryFunction<T> fn) 



If the method is implemented without closure, we will have an implementation like this:
    
    public FareList discount(final double discountRate) {
        FareList result = new FareList();
        ListUtil.map(listOfFares, new UnaryFunction<Double>() {
            public Double apply(Double x) {
                return (1 - discountRate) * x;
            }
        });
        return result;
    }



Note how final is needed by the anonymous inner class UnaryFunction. The final qualifier is needed for the apply method to access a local variable in the same block as the creation of the UnaryFunction.

It turns out, fortunately, that we don't have the same limitation in closure. Local variable can be accessed inside the closure without being qualified by final qualifier. This is the code of discount method implemented using closure:


public FareList discount(double discountRate) {
    FareList result = new FareList();
    result.listOfFares =
       ListUtil.map(listOfFares, #(x) {(1 - discountRate) * x} );
    return result;
}



(Note that I don't use the type in the definition of closure, that is instead of #(Double x) {(1 - discountRate) * x} , I use 
#(x) {(1 - discountRate) * x}, that is, I omit the type of the parameter. Indeed, it is possible to omit the type for parameter of closure (lambda) and let the compiler infers the type).

I have shown above that closure can use the value from its environment, in this case from the local variable. Closure may also access the instance variable (property) of the class. 

The example below shows an example where discount method accesses maxDiscountRate that makes sure that the discountRate passed as parameter to be less than maxDiscountRate property of FareList class. 




public FareList discount(double discountRate) {
    FareList result = new FareList();
    result.listOfFares =
       ListUtil.map(listOfFares, 
          #(x) {(1 - (Math.min(discountRate, maxDiscountRate)) * x} );
    return result;
}




What about that ? Quite nice, isn't it?

No comments: