Safely call setter after getter chain eg foo.getX().getY().setZ(…);

  • A+
Category:Languages

How do I safely call setter after getter chain eg foo.getX().getY().setZ(...);? For example, suppose I have a nested POJO, and I want to be able to set a field of a nested object.

Foo foo = ... foo.getX().getY().setZ(...); 

I want the behavior to be such that if X and Y do not exist then they are created automatically; otherwise it reuses the existing object.

In other words, I want it to be behave equivalent to

Foo foo = ... X x = foo.getX(); if (x == null) {    x = new X();   foo.setX(x); }  Y y = x.getY(); if (y == null) {   y = newY();   x.setY(y); }  y.setZ(...); 

I'm wondering if there is a trick out there using reflection/functional that comes close to this.

I also have the following constraints:

  • I cannot modify any of the classes
  • The solution must know about only the public getters and setters, not the private instance variables
  • I want the getter to modify the internal state only when specifically requested; I don't want x = foo.getX() to modify foo.

 


Use functional programming. Create a method that accepts a getter, a setter and a supplier for the default value, that returns a getter encapsulating the logic you need:

public static <T, U> Function<T, U> getOrSetDefault(         Function<T, U> getter,         BiConsumer<T, U> setter,         Supplier<U> defaultValue) {      return t -> {         U u = getter.apply(t);         if (u == null) {             u = defaultValue.get();             setter.accept(t, u);         }         return u;     }; } 

Then create these decorated getters:

Function<Foo, X> getX = getOrSetDefault(Foo::getX, Foo::setX, X::new); Function<X, Y> getY = getOrSetDefault(X::getY, X::setY, Y::new); 

Finally, chain them and apply the resulting function passing in your foo instance as an argument:

Foo foo = ... getX.andThen(getY).apply(foo).setZ(...); 

EDIT: This assumes that both X and Y have a no-args constructor that is referenced by X::new and Y::new, respectively. But you could use anything as the Supplier, i.e. an already created instance, or the return value of a method, etc.

Comment

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: