#

Sunday, June 4, 2017

Chaining with Java 8

  String name = "Arjun"; int age = 35;
  Employee e = new Employee();
     obj.setName(name);
     obj.setAge(age);

  organization.add (e);

The problem with setter chaining

In the above, one may choose to modify the setters with a return type same as the Object but for many reasons (see previous blog posts), that is incorrect and ambiguous theoretically speaking in terms of type.

Solution

To solve the above elegantly here is a solution I conjured using Lambda expressions and Functional interfaces.
First define a Functional Interface that will make chaining a breeze for any POJO.
import java.util.function.Consumer;

public interface Chainable {
 default T chain(Consumer c) {
  c.accept((T)this);
  return (T)this;
 }
}

Now, for code that uses it assuming our same Employee object implements Chainable
 String name = "Arjun"; int age = 35;
 organization.add(new Employee()
  .chain(e -> e.setName(name))
  .chain(e -> e.setAge(age))
 );

What just happened?

One can look @ the above also as :
    final String name = "Arjun"; final int age =35;
    organization.add(new Employee()
       .chain(new Consumer() {
         public void accept(Employee e) {
           e.setName(name);     
         }
       })
       .chain(new Consumer() {
         public void accept(Employee e) {
           e.setAge(age);     
         }
       })
    );
Notice another thing; when you use anonymous classes you need to declare the variables as final. With lambda expressions in addition reduced verbosity you also do not have to worry about variable scope. Which is pretty neat. I have often not been a fan of paradigms that only reduce verbosity. however there is more going on here conceptually. Other topics worth reading about are Monads.

Chaining other objects that cannot implement our Chainable

/**
 * Make it possible to {@link Chain} objects that do not implement {@link Chain}
 * <br>
 * Sometimes even Lambda expressions can make a mess of things.
 * <br><br>
 * Example:
 * <code><pre>
 Map map = new ChainableWrapper<Map>(new HashMap())
   .flatchain(m -> m.put("Name", "Value"))
   .flatchain(m -> m.put("Name2", "Value2"))
   .get();
 ;  
 * </pre></code>
 * 
 * @author Arjun Dhar
 */
public class ChainableWrapper<T> implements Chainable<ChainableWrapper<T>> {
 private T o; 
 public ChainableWrapper(T o) {this.o=o;} 
 public T get() {return o;}
 
 public ChainableWrapper<T> wrapperchain(Consumer<T> c) {
  c.accept(this.get());
  return this;
 }
}
Usage:
 @Test
 public void chainableWrapperTest() {
  Map map = new ChainableWrapper<Map>(new HashMap())
    .wrapperchain(m -> m.put("Name", "Value"))
    .wrapperchain(m -> m.put("Name2", "Value2"))
    .get();
  ;
  
  Assert.assertEquals("Value", map.get("Name"));
  Assert.assertEquals("Value2", map.get("Name2"));
 }
 }

No comments:

Post a Comment