Friday, August 9, 2013

Almost named method arguments in JDK 8

Sometimes it would be really nice to have named method parameter in Java, this doesn't look likely for a long while but there is always another little work around, for example using a builder pattern to get similar behaviour, that will bridge the gap for a little bit. It occurred to me that using the Lambda support in JDK 8 you could get something very close without the boiler plate required by some of the other methods.

So consider this simple Facade class, that presents a method with a variable list of parameters. Note the variant of the method that takes in a Consumer object - this is what we are going to invoke with a Lambda later on.

package client;

import java.util.function.Consumer;

public class Facade {
    
    public static void invoke(Consumer<Parameter> op) {
        
        Parameter p = new Parameter();
        op.accept(new Parameter());
        invoke(p);        
    }
    
    public static void invoke(Parameter p) {

        // Defensive copy and validate
        p = p.clone();
        p.validate();
        
        // ...
    }

    
    public static class Parameter implements Cloneable {
        public String value1, value2, value3;
        
        public Parameter validate() {
            // ...
            
            return this;
        }

        public Parameter clone() {
            // We can use vanilla clone here, for more complicated mutable objects
            // you might need to perform a deeper copy
            try {
                return (Parameter) super.clone();
            } catch (CloneNotSupportedException e) {
                throw new RuntimeException(e);
            }
        }
    }
    
}

So once you have this in place, you can use Lambda notation to call the invoke method with how every many parameters as you might think suitable:

...
{

    public static void main(String[] args) {
        
        // Single parameter version
        Facade.invoke(p ->  p.value1="x" );
        // Multiple parameter version
        Facade.invoke(p -> { p.value1="x"; p.value2="y";});
    }
}

Of course the eagle eyed of your will recognise that the same pattern, minus the Consumer method of course, with an earlier JDK. Given the number of java developers out there some-one probably will have probably already invented this pattern; but you never know and if you don't write it down the thought it didn't happen.

...
{

    public static void main(String[] args) {
        
        // Using an anonymous inner class pre JDK 8
        Facade.invoke(new Parameter() {{
            value1 = "x";
            value2 = "y";
        }});
    }
}

2 comments:

Andreas Haufler said...

Nice idea, but still looks a bit messy to me. I still prefer fluent APIs like X.length(5).width(8).make() and therelike. What I immediatelly would change, is the default pattern for setters from: void setX(int x)... to: T setX(int x) where T is the current class, so each setter essentially returns this. That'll give us a fluent API for free...

Gerard Davison said...

Thats a fair comment, I am not 100% sure about it how based on other comments. The one thing I will say is that if you used something like LambdaJ that you could get those for free with far less typing.

I guess that day I was focused on reducing boilerplate.