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










9















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.









share|improve this question
























  • So you don't want to touch the Foo, X, Y, ... classes, right? AOP is probably your best bet.

    – Robby Cornelissen
    Nov 15 '18 at 8:30







  • 1





    The keyword to google here is probably "lenses".

    – Thilo
    Nov 15 '18 at 8:34











  • You can create a getter in class Foo in such a way that it returns X if not null else retruns a new object of X. X getX() return x == null ? return new X() : x;

    – efex09
    Nov 15 '18 at 8:51











  • @efex09 "I cannot modify any of the classes"

    – Marco Bonelli
    Nov 15 '18 at 8:59






  • 1





    @Thilo are you referring to Haskell's Lenses (eg stackoverflow.com/questions/8307370/functional-lenses)? I'm not familiar with Haskell so I'm not sure if that solves the problem

    – Display Name
    Nov 15 '18 at 9:27















9















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.









share|improve this question
























  • So you don't want to touch the Foo, X, Y, ... classes, right? AOP is probably your best bet.

    – Robby Cornelissen
    Nov 15 '18 at 8:30







  • 1





    The keyword to google here is probably "lenses".

    – Thilo
    Nov 15 '18 at 8:34











  • You can create a getter in class Foo in such a way that it returns X if not null else retruns a new object of X. X getX() return x == null ? return new X() : x;

    – efex09
    Nov 15 '18 at 8:51











  • @efex09 "I cannot modify any of the classes"

    – Marco Bonelli
    Nov 15 '18 at 8:59






  • 1





    @Thilo are you referring to Haskell's Lenses (eg stackoverflow.com/questions/8307370/functional-lenses)? I'm not familiar with Haskell so I'm not sure if that solves the problem

    – Display Name
    Nov 15 '18 at 9:27













9












9








9


2






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.









share|improve this question
















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.






java reflection java-8 functional-programming






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 15 '18 at 9:26









Mark Rotteveel

61.3k1478121




61.3k1478121










asked Nov 15 '18 at 8:26









Display NameDisplay Name

4082515




4082515












  • So you don't want to touch the Foo, X, Y, ... classes, right? AOP is probably your best bet.

    – Robby Cornelissen
    Nov 15 '18 at 8:30







  • 1





    The keyword to google here is probably "lenses".

    – Thilo
    Nov 15 '18 at 8:34











  • You can create a getter in class Foo in such a way that it returns X if not null else retruns a new object of X. X getX() return x == null ? return new X() : x;

    – efex09
    Nov 15 '18 at 8:51











  • @efex09 "I cannot modify any of the classes"

    – Marco Bonelli
    Nov 15 '18 at 8:59






  • 1





    @Thilo are you referring to Haskell's Lenses (eg stackoverflow.com/questions/8307370/functional-lenses)? I'm not familiar with Haskell so I'm not sure if that solves the problem

    – Display Name
    Nov 15 '18 at 9:27

















  • So you don't want to touch the Foo, X, Y, ... classes, right? AOP is probably your best bet.

    – Robby Cornelissen
    Nov 15 '18 at 8:30







  • 1





    The keyword to google here is probably "lenses".

    – Thilo
    Nov 15 '18 at 8:34











  • You can create a getter in class Foo in such a way that it returns X if not null else retruns a new object of X. X getX() return x == null ? return new X() : x;

    – efex09
    Nov 15 '18 at 8:51











  • @efex09 "I cannot modify any of the classes"

    – Marco Bonelli
    Nov 15 '18 at 8:59






  • 1





    @Thilo are you referring to Haskell's Lenses (eg stackoverflow.com/questions/8307370/functional-lenses)? I'm not familiar with Haskell so I'm not sure if that solves the problem

    – Display Name
    Nov 15 '18 at 9:27
















So you don't want to touch the Foo, X, Y, ... classes, right? AOP is probably your best bet.

– Robby Cornelissen
Nov 15 '18 at 8:30






So you don't want to touch the Foo, X, Y, ... classes, right? AOP is probably your best bet.

– Robby Cornelissen
Nov 15 '18 at 8:30





1




1





The keyword to google here is probably "lenses".

– Thilo
Nov 15 '18 at 8:34





The keyword to google here is probably "lenses".

– Thilo
Nov 15 '18 at 8:34













You can create a getter in class Foo in such a way that it returns X if not null else retruns a new object of X. X getX() return x == null ? return new X() : x;

– efex09
Nov 15 '18 at 8:51





You can create a getter in class Foo in such a way that it returns X if not null else retruns a new object of X. X getX() return x == null ? return new X() : x;

– efex09
Nov 15 '18 at 8:51













@efex09 "I cannot modify any of the classes"

– Marco Bonelli
Nov 15 '18 at 8:59





@efex09 "I cannot modify any of the classes"

– Marco Bonelli
Nov 15 '18 at 8:59




1




1





@Thilo are you referring to Haskell's Lenses (eg stackoverflow.com/questions/8307370/functional-lenses)? I'm not familiar with Haskell so I'm not sure if that solves the problem

– Display Name
Nov 15 '18 at 9:27





@Thilo are you referring to Haskell's Lenses (eg stackoverflow.com/questions/8307370/functional-lenses)? I'm not familiar with Haskell so I'm not sure if that solves the problem

– Display Name
Nov 15 '18 at 9:27












4 Answers
4






active

oldest

votes


















6














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.






share|improve this answer




















  • 2





    Note that nothing here assumes the default value for missing objects is what the no-args constructor returns (or that such a constructor exists). You can easily plug in something other than X::new into your accessor definitions.

    – Thilo
    Nov 15 '18 at 13:42











  • @Thilo Yes, that is an advantage of this approach. Will add a note.

    – Federico Peralta Schaffner
    Nov 15 '18 at 14:18












  • I ended up using an approach that was similar to yours. However, I used reflection to avoid having to pass the setter and the default values. See my Answer for more info.

    – Display Name
    Nov 17 '18 at 6:21


















3














TL;DR: Don't try to force functional Java where there clearly is no place for it.




The only way you can do this functionally in Java 8 without modifying any of the classes is using Optionals and their .orElse() method. It gets really long really quick, but it's the only way that actually makes sense using functional if you want to do it in one line only.



Optional.ofNullable(foo.getX()).orElseGet(() -> foo.setX(new X()); return foo.getX(); ).setY(...);


If foo.setX() also returns the setted value it can be simplified as:



Optional.ofNullable(foo.getX()).orElseGet(() -> foo.setX(new X())).setY(...);


This is the only generic and functional way of doing it that I can think of. Stated the above, you can clearly see that this becomes huge and ugly even for just a chain of two getters, so I wouldn't advise it. I would definitely suggest you to use the classic multi-statement approach if you have to chain more than one call.



Another option, even thought not really that functional, is to use the tristate operator, still only if the setter returns the setted value:



(foo.getX() == null ? foo.setX(new X()) : foo.getX()).setY(...);


This has the probably unwanted side effect of calling the getter twice if the element is found, which you may not like, but could be possibly ignored if the getter caches the value somehow.






share|improve this answer

























  • Yep, that becomes quite unwieldy quite fast. Thanks for the suggestion though!

    – Display Name
    Nov 15 '18 at 9:25











  • @DisplayName I added another option.

    – Marco Bonelli
    Nov 15 '18 at 9:29


















2














To start off I just want to mention that this probably isn't the best solution and I'm sure there are ways to optimize this. That said, I wanted to try my hand at CGLIB and ObjenesisHelper again.



Using CGLIB and ObjenesisHelper we can wrap the data object in a proxy which will intercept the get methods. Using this interceptor we can add the logic you described in your post. Lets start off by assume these are our data types (using lombok for brevity).



@Data class W private X x; 
@Data class X private Y y;
@Data class Y private Z z;
@Data class Z private int alpha;


Our final solution can be used like the following:



public static void main(String args) 
final W w = ProxyUtil.withLazyDefaults(new W());
System.out.println(w.getX().getY().getZ().getAlpha());



Implementation



Currently, if we try to invoke new W().getX().getY().getZ().getAlpha() we will get a NullPointerException when invoking getY() because getX() returned null. Even if we manage to produce a default X value, we will still need a default Y value to not get a null pointer on getZ() and getAlpha() and so forth. The proxy we create needs to be generic and be able to wrap its sub components recursively.



Okay, so lets start. The first thing we need to do is create a MethodInterceptor. Whenever any call hits our proxy instance it will perform the logic of our MethodInterceptor. We need to first determine if the method called is a getter. If not we will ignore it. During this getter call, if the value is not present in our data we will create it and update the object. If the value contained by the getter is an original unwrapped class, we will replace it with a wraped version. Finally we will return the wrapped instance. Edit I updated this to not inject wrapped instances into the real Data objects. This will be less performant if the object is accessed mutliple times this way



public class ProxyUtil 
public static <T> T withLazyDefaults(final T data)
final MethodInterceptor interceptor = (object, method, args, proxy) ->
if (method.getName().startsWith("get"))
final Class<?> returnType = method.getReturnType();
Object response = method.invoke(data, args);
if (response == null)
response = returnType.newInstance();
data.getClass()
.getDeclaredMethod(
method.getName().replaceFirst("get", "set"),
returnType)
.invoke(data, response);

if (!returnType.isPrimitive())
response = withLazyDefaults(response);

return response;

return method.invoke(data, args);
;
...


The rest of this method involves using CGLIB and Objenisis Helper to construct the wrapper instance. CGLib will allow you to proxy both classes and interfaces and ObjenesisHelper will allow you to construct an instance of a class without having to invoke a constructor. See here for a CGLib example and here for a ObjenesisHelper example.



 ...
final Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(data.getClass());
final Set<Class<?>> interfaces = new LinkedHashSet<>();
if (data.getClass().isInterface())
interfaces.add(data.getClass());

interfaces.addAll(Arrays.asList(data.getClass().getInterfaces()));
enhancer.setInterfaces(interfaces.toArray(new Class[interfaces.size()]));
enhancer.setCallbackType(interceptor.getClass());

final Class<?> proxyClass = enhancer.createClass();
Enhancer.registerStaticCallbacks(proxyClass, new Callbackinterceptor);
return (T) ObjenesisHelper.newInstance(proxyClass);




Caveats



  • This is not a thread safe operation.

  • Reflection will slow down your code.

  • Better error handling needs to added for the reflection calls.

  • If a class does not have a no-arg constructor this will not work.

  • Does not account for inheritance of data classes

  • This could be best effort by checking for a no-arg ctor/setter first.





share|improve this answer
































    1














    I ended up using a combination of functional and reflection and tried to make the interface similar to Java's Optional. Here is an example of how I would write foo.getX().getY().setZ(val);



    MutableGetter.of(foo).map(Foo::getX).map(x::getY).get().setZ(val);


    This is the code (It's still WIP).
    I used reflection to avoid having to pass the setter and constructor



    import java.lang.reflect.Constructor;
    import java.lang.reflect.Method;
    import java.util.function.BiConsumer;
    import java.util.function.Function;
    import java.util.function.Supplier;

    import javassist.util.proxy.MethodHandler;
    import javassist.util.proxy.ProxyFactory;
    import lombok.Getter;
    import lombok.NonNull;

    public class MutableGetter<T>

    private T object;

    private MutableGetter(T object)

    this.object = object;


    public static <T> MutableGetter<T> of(@NonNull T object)

    return new MutableGetter<>(object);


    public <U> MutableGetter<U> map(Function<T, U> getter)

    Method getterMethod = getGetterMethod(object.getClass(), getter);
    BiConsumer<T, U> setter = getSetter(getterMethod);
    Supplier<U> defaultValue = getDefaultValue(getterMethod);

    U nextObject = getter.apply(object);
    if (nextObject == null)
    nextObject = defaultValue.get();
    setter.accept(object, nextObject);


    return new MutableGetter<>(nextObject);


    public T get()

    return object;


    private static <U> Supplier<U> getDefaultValue(Method getterMethod)

    return () ->
    try
    Constructor<?> constructor = getterMethod.getReturnType().getConstructor();
    constructor.setAccessible(true);
    return (U) constructor.newInstance();
    catch (Exception e)
    throw new IllegalStateException(e);

    ;


    private static <T, U> BiConsumer<T,U> getSetter(Method getterMethod)

    return (obj, arg) ->
    Method setterMethod = getSetterFromGetter(getterMethod);
    setterMethod.setAccessible(true);

    try
    setterMethod.invoke(obj, arg);
    catch (Exception e)
    throw new IllegalStateException(e);

    ;


    private static Method getSetterFromGetter(Method getter)

    if (!getter.getName().startsWith("get"))
    throw new IllegalStateException("The getter method must start with 'get'");


    String setterName = getter.getName().replaceFirst("get", "set");

    Method methods = getter.getDeclaringClass().getMethods();

    for (Method method: methods)
    if (method.getName().equals(setterName))
    return method;



    throw new IllegalStateException(String.format("Couldn't find setter in class %s with name %s", getter.getDeclaringClass(), setterName));


    private static <T, U> Method getGetterMethod(Class<?> clazz, Function<T, U> getter)

    ProxyFactory proxyFactory = new ProxyFactory();
    proxyFactory.setSuperclass(clazz);

    MethodRecorder methodRecorder = new MethodRecorder();

    T proxy;
    try
    proxy = (T) proxyFactory.create(new Class<?>[0], new Object[0], methodRecorder);
    catch (Exception e)
    throw new IllegalStateException(e);


    getter.apply(proxy);

    return methodRecorder.getLastInvokedMethod();


    private static class MethodRecorder implements MethodHandler

    @Getter
    private Method lastInvokedMethod;

    @Override
    public Object invoke(Object self, Method thisMethod, Method proceed, Object args)

    this.lastInvokedMethod = thisMethod;
    return null; // the result is ignored






    Let me know if you have any suggestions






    share|improve this answer






















      Your Answer






      StackExchange.ifUsing("editor", function ()
      StackExchange.using("externalEditor", function ()
      StackExchange.using("snippets", function ()
      StackExchange.snippets.init();
      );
      );
      , "code-snippets");

      StackExchange.ready(function()
      var channelOptions =
      tags: "".split(" "),
      id: "1"
      ;
      initTagRenderer("".split(" "), "".split(" "), channelOptions);

      StackExchange.using("externalEditor", function()
      // Have to fire editor after snippets, if snippets enabled
      if (StackExchange.settings.snippets.snippetsEnabled)
      StackExchange.using("snippets", function()
      createEditor();
      );

      else
      createEditor();

      );

      function createEditor()
      StackExchange.prepareEditor(
      heartbeatType: 'answer',
      autoActivateHeartbeat: false,
      convertImagesToLinks: true,
      noModals: true,
      showLowRepImageUploadWarning: true,
      reputationToPostImages: 10,
      bindNavPrevention: true,
      postfix: "",
      imageUploader:
      brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
      contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
      allowUrls: true
      ,
      onDemand: true,
      discardSelector: ".discard-answer"
      ,immediatelyShowMarkdownHelp:true
      );



      );













      draft saved

      draft discarded


















      StackExchange.ready(
      function ()
      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53315143%2fsafely-call-setter-after-getter-chain-eg-foo-getx-gety-setz%23new-answer', 'question_page');

      );

      Post as a guest















      Required, but never shown

























      4 Answers
      4






      active

      oldest

      votes








      4 Answers
      4






      active

      oldest

      votes









      active

      oldest

      votes






      active

      oldest

      votes









      6














      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.






      share|improve this answer




















      • 2





        Note that nothing here assumes the default value for missing objects is what the no-args constructor returns (or that such a constructor exists). You can easily plug in something other than X::new into your accessor definitions.

        – Thilo
        Nov 15 '18 at 13:42











      • @Thilo Yes, that is an advantage of this approach. Will add a note.

        – Federico Peralta Schaffner
        Nov 15 '18 at 14:18












      • I ended up using an approach that was similar to yours. However, I used reflection to avoid having to pass the setter and the default values. See my Answer for more info.

        – Display Name
        Nov 17 '18 at 6:21















      6














      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.






      share|improve this answer




















      • 2





        Note that nothing here assumes the default value for missing objects is what the no-args constructor returns (or that such a constructor exists). You can easily plug in something other than X::new into your accessor definitions.

        – Thilo
        Nov 15 '18 at 13:42











      • @Thilo Yes, that is an advantage of this approach. Will add a note.

        – Federico Peralta Schaffner
        Nov 15 '18 at 14:18












      • I ended up using an approach that was similar to yours. However, I used reflection to avoid having to pass the setter and the default values. See my Answer for more info.

        – Display Name
        Nov 17 '18 at 6:21













      6












      6








      6







      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.






      share|improve this answer















      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.







      share|improve this answer














      share|improve this answer



      share|improve this answer








      edited Nov 15 '18 at 14:20

























      answered Nov 15 '18 at 13:26









      Federico Peralta SchaffnerFederico Peralta Schaffner

      23.5k43778




      23.5k43778







      • 2





        Note that nothing here assumes the default value for missing objects is what the no-args constructor returns (or that such a constructor exists). You can easily plug in something other than X::new into your accessor definitions.

        – Thilo
        Nov 15 '18 at 13:42











      • @Thilo Yes, that is an advantage of this approach. Will add a note.

        – Federico Peralta Schaffner
        Nov 15 '18 at 14:18












      • I ended up using an approach that was similar to yours. However, I used reflection to avoid having to pass the setter and the default values. See my Answer for more info.

        – Display Name
        Nov 17 '18 at 6:21












      • 2





        Note that nothing here assumes the default value for missing objects is what the no-args constructor returns (or that such a constructor exists). You can easily plug in something other than X::new into your accessor definitions.

        – Thilo
        Nov 15 '18 at 13:42











      • @Thilo Yes, that is an advantage of this approach. Will add a note.

        – Federico Peralta Schaffner
        Nov 15 '18 at 14:18












      • I ended up using an approach that was similar to yours. However, I used reflection to avoid having to pass the setter and the default values. See my Answer for more info.

        – Display Name
        Nov 17 '18 at 6:21







      2




      2





      Note that nothing here assumes the default value for missing objects is what the no-args constructor returns (or that such a constructor exists). You can easily plug in something other than X::new into your accessor definitions.

      – Thilo
      Nov 15 '18 at 13:42





      Note that nothing here assumes the default value for missing objects is what the no-args constructor returns (or that such a constructor exists). You can easily plug in something other than X::new into your accessor definitions.

      – Thilo
      Nov 15 '18 at 13:42













      @Thilo Yes, that is an advantage of this approach. Will add a note.

      – Federico Peralta Schaffner
      Nov 15 '18 at 14:18






      @Thilo Yes, that is an advantage of this approach. Will add a note.

      – Federico Peralta Schaffner
      Nov 15 '18 at 14:18














      I ended up using an approach that was similar to yours. However, I used reflection to avoid having to pass the setter and the default values. See my Answer for more info.

      – Display Name
      Nov 17 '18 at 6:21





      I ended up using an approach that was similar to yours. However, I used reflection to avoid having to pass the setter and the default values. See my Answer for more info.

      – Display Name
      Nov 17 '18 at 6:21













      3














      TL;DR: Don't try to force functional Java where there clearly is no place for it.




      The only way you can do this functionally in Java 8 without modifying any of the classes is using Optionals and their .orElse() method. It gets really long really quick, but it's the only way that actually makes sense using functional if you want to do it in one line only.



      Optional.ofNullable(foo.getX()).orElseGet(() -> foo.setX(new X()); return foo.getX(); ).setY(...);


      If foo.setX() also returns the setted value it can be simplified as:



      Optional.ofNullable(foo.getX()).orElseGet(() -> foo.setX(new X())).setY(...);


      This is the only generic and functional way of doing it that I can think of. Stated the above, you can clearly see that this becomes huge and ugly even for just a chain of two getters, so I wouldn't advise it. I would definitely suggest you to use the classic multi-statement approach if you have to chain more than one call.



      Another option, even thought not really that functional, is to use the tristate operator, still only if the setter returns the setted value:



      (foo.getX() == null ? foo.setX(new X()) : foo.getX()).setY(...);


      This has the probably unwanted side effect of calling the getter twice if the element is found, which you may not like, but could be possibly ignored if the getter caches the value somehow.






      share|improve this answer

























      • Yep, that becomes quite unwieldy quite fast. Thanks for the suggestion though!

        – Display Name
        Nov 15 '18 at 9:25











      • @DisplayName I added another option.

        – Marco Bonelli
        Nov 15 '18 at 9:29















      3














      TL;DR: Don't try to force functional Java where there clearly is no place for it.




      The only way you can do this functionally in Java 8 without modifying any of the classes is using Optionals and their .orElse() method. It gets really long really quick, but it's the only way that actually makes sense using functional if you want to do it in one line only.



      Optional.ofNullable(foo.getX()).orElseGet(() -> foo.setX(new X()); return foo.getX(); ).setY(...);


      If foo.setX() also returns the setted value it can be simplified as:



      Optional.ofNullable(foo.getX()).orElseGet(() -> foo.setX(new X())).setY(...);


      This is the only generic and functional way of doing it that I can think of. Stated the above, you can clearly see that this becomes huge and ugly even for just a chain of two getters, so I wouldn't advise it. I would definitely suggest you to use the classic multi-statement approach if you have to chain more than one call.



      Another option, even thought not really that functional, is to use the tristate operator, still only if the setter returns the setted value:



      (foo.getX() == null ? foo.setX(new X()) : foo.getX()).setY(...);


      This has the probably unwanted side effect of calling the getter twice if the element is found, which you may not like, but could be possibly ignored if the getter caches the value somehow.






      share|improve this answer

























      • Yep, that becomes quite unwieldy quite fast. Thanks for the suggestion though!

        – Display Name
        Nov 15 '18 at 9:25











      • @DisplayName I added another option.

        – Marco Bonelli
        Nov 15 '18 at 9:29













      3












      3








      3







      TL;DR: Don't try to force functional Java where there clearly is no place for it.




      The only way you can do this functionally in Java 8 without modifying any of the classes is using Optionals and their .orElse() method. It gets really long really quick, but it's the only way that actually makes sense using functional if you want to do it in one line only.



      Optional.ofNullable(foo.getX()).orElseGet(() -> foo.setX(new X()); return foo.getX(); ).setY(...);


      If foo.setX() also returns the setted value it can be simplified as:



      Optional.ofNullable(foo.getX()).orElseGet(() -> foo.setX(new X())).setY(...);


      This is the only generic and functional way of doing it that I can think of. Stated the above, you can clearly see that this becomes huge and ugly even for just a chain of two getters, so I wouldn't advise it. I would definitely suggest you to use the classic multi-statement approach if you have to chain more than one call.



      Another option, even thought not really that functional, is to use the tristate operator, still only if the setter returns the setted value:



      (foo.getX() == null ? foo.setX(new X()) : foo.getX()).setY(...);


      This has the probably unwanted side effect of calling the getter twice if the element is found, which you may not like, but could be possibly ignored if the getter caches the value somehow.






      share|improve this answer















      TL;DR: Don't try to force functional Java where there clearly is no place for it.




      The only way you can do this functionally in Java 8 without modifying any of the classes is using Optionals and their .orElse() method. It gets really long really quick, but it's the only way that actually makes sense using functional if you want to do it in one line only.



      Optional.ofNullable(foo.getX()).orElseGet(() -> foo.setX(new X()); return foo.getX(); ).setY(...);


      If foo.setX() also returns the setted value it can be simplified as:



      Optional.ofNullable(foo.getX()).orElseGet(() -> foo.setX(new X())).setY(...);


      This is the only generic and functional way of doing it that I can think of. Stated the above, you can clearly see that this becomes huge and ugly even for just a chain of two getters, so I wouldn't advise it. I would definitely suggest you to use the classic multi-statement approach if you have to chain more than one call.



      Another option, even thought not really that functional, is to use the tristate operator, still only if the setter returns the setted value:



      (foo.getX() == null ? foo.setX(new X()) : foo.getX()).setY(...);


      This has the probably unwanted side effect of calling the getter twice if the element is found, which you may not like, but could be possibly ignored if the getter caches the value somehow.







      share|improve this answer














      share|improve this answer



      share|improve this answer








      edited Nov 15 '18 at 13:08

























      answered Nov 15 '18 at 9:19









      Marco BonelliMarco Bonelli

      23.7k116474




      23.7k116474












      • Yep, that becomes quite unwieldy quite fast. Thanks for the suggestion though!

        – Display Name
        Nov 15 '18 at 9:25











      • @DisplayName I added another option.

        – Marco Bonelli
        Nov 15 '18 at 9:29

















      • Yep, that becomes quite unwieldy quite fast. Thanks for the suggestion though!

        – Display Name
        Nov 15 '18 at 9:25











      • @DisplayName I added another option.

        – Marco Bonelli
        Nov 15 '18 at 9:29
















      Yep, that becomes quite unwieldy quite fast. Thanks for the suggestion though!

      – Display Name
      Nov 15 '18 at 9:25





      Yep, that becomes quite unwieldy quite fast. Thanks for the suggestion though!

      – Display Name
      Nov 15 '18 at 9:25













      @DisplayName I added another option.

      – Marco Bonelli
      Nov 15 '18 at 9:29





      @DisplayName I added another option.

      – Marco Bonelli
      Nov 15 '18 at 9:29











      2














      To start off I just want to mention that this probably isn't the best solution and I'm sure there are ways to optimize this. That said, I wanted to try my hand at CGLIB and ObjenesisHelper again.



      Using CGLIB and ObjenesisHelper we can wrap the data object in a proxy which will intercept the get methods. Using this interceptor we can add the logic you described in your post. Lets start off by assume these are our data types (using lombok for brevity).



      @Data class W private X x; 
      @Data class X private Y y;
      @Data class Y private Z z;
      @Data class Z private int alpha;


      Our final solution can be used like the following:



      public static void main(String args) 
      final W w = ProxyUtil.withLazyDefaults(new W());
      System.out.println(w.getX().getY().getZ().getAlpha());



      Implementation



      Currently, if we try to invoke new W().getX().getY().getZ().getAlpha() we will get a NullPointerException when invoking getY() because getX() returned null. Even if we manage to produce a default X value, we will still need a default Y value to not get a null pointer on getZ() and getAlpha() and so forth. The proxy we create needs to be generic and be able to wrap its sub components recursively.



      Okay, so lets start. The first thing we need to do is create a MethodInterceptor. Whenever any call hits our proxy instance it will perform the logic of our MethodInterceptor. We need to first determine if the method called is a getter. If not we will ignore it. During this getter call, if the value is not present in our data we will create it and update the object. If the value contained by the getter is an original unwrapped class, we will replace it with a wraped version. Finally we will return the wrapped instance. Edit I updated this to not inject wrapped instances into the real Data objects. This will be less performant if the object is accessed mutliple times this way



      public class ProxyUtil 
      public static <T> T withLazyDefaults(final T data)
      final MethodInterceptor interceptor = (object, method, args, proxy) ->
      if (method.getName().startsWith("get"))
      final Class<?> returnType = method.getReturnType();
      Object response = method.invoke(data, args);
      if (response == null)
      response = returnType.newInstance();
      data.getClass()
      .getDeclaredMethod(
      method.getName().replaceFirst("get", "set"),
      returnType)
      .invoke(data, response);

      if (!returnType.isPrimitive())
      response = withLazyDefaults(response);

      return response;

      return method.invoke(data, args);
      ;
      ...


      The rest of this method involves using CGLIB and Objenisis Helper to construct the wrapper instance. CGLib will allow you to proxy both classes and interfaces and ObjenesisHelper will allow you to construct an instance of a class without having to invoke a constructor. See here for a CGLib example and here for a ObjenesisHelper example.



       ...
      final Enhancer enhancer = new Enhancer();
      enhancer.setSuperclass(data.getClass());
      final Set<Class<?>> interfaces = new LinkedHashSet<>();
      if (data.getClass().isInterface())
      interfaces.add(data.getClass());

      interfaces.addAll(Arrays.asList(data.getClass().getInterfaces()));
      enhancer.setInterfaces(interfaces.toArray(new Class[interfaces.size()]));
      enhancer.setCallbackType(interceptor.getClass());

      final Class<?> proxyClass = enhancer.createClass();
      Enhancer.registerStaticCallbacks(proxyClass, new Callbackinterceptor);
      return (T) ObjenesisHelper.newInstance(proxyClass);




      Caveats



      • This is not a thread safe operation.

      • Reflection will slow down your code.

      • Better error handling needs to added for the reflection calls.

      • If a class does not have a no-arg constructor this will not work.

      • Does not account for inheritance of data classes

      • This could be best effort by checking for a no-arg ctor/setter first.





      share|improve this answer





























        2














        To start off I just want to mention that this probably isn't the best solution and I'm sure there are ways to optimize this. That said, I wanted to try my hand at CGLIB and ObjenesisHelper again.



        Using CGLIB and ObjenesisHelper we can wrap the data object in a proxy which will intercept the get methods. Using this interceptor we can add the logic you described in your post. Lets start off by assume these are our data types (using lombok for brevity).



        @Data class W private X x; 
        @Data class X private Y y;
        @Data class Y private Z z;
        @Data class Z private int alpha;


        Our final solution can be used like the following:



        public static void main(String args) 
        final W w = ProxyUtil.withLazyDefaults(new W());
        System.out.println(w.getX().getY().getZ().getAlpha());



        Implementation



        Currently, if we try to invoke new W().getX().getY().getZ().getAlpha() we will get a NullPointerException when invoking getY() because getX() returned null. Even if we manage to produce a default X value, we will still need a default Y value to not get a null pointer on getZ() and getAlpha() and so forth. The proxy we create needs to be generic and be able to wrap its sub components recursively.



        Okay, so lets start. The first thing we need to do is create a MethodInterceptor. Whenever any call hits our proxy instance it will perform the logic of our MethodInterceptor. We need to first determine if the method called is a getter. If not we will ignore it. During this getter call, if the value is not present in our data we will create it and update the object. If the value contained by the getter is an original unwrapped class, we will replace it with a wraped version. Finally we will return the wrapped instance. Edit I updated this to not inject wrapped instances into the real Data objects. This will be less performant if the object is accessed mutliple times this way



        public class ProxyUtil 
        public static <T> T withLazyDefaults(final T data)
        final MethodInterceptor interceptor = (object, method, args, proxy) ->
        if (method.getName().startsWith("get"))
        final Class<?> returnType = method.getReturnType();
        Object response = method.invoke(data, args);
        if (response == null)
        response = returnType.newInstance();
        data.getClass()
        .getDeclaredMethod(
        method.getName().replaceFirst("get", "set"),
        returnType)
        .invoke(data, response);

        if (!returnType.isPrimitive())
        response = withLazyDefaults(response);

        return response;

        return method.invoke(data, args);
        ;
        ...


        The rest of this method involves using CGLIB and Objenisis Helper to construct the wrapper instance. CGLib will allow you to proxy both classes and interfaces and ObjenesisHelper will allow you to construct an instance of a class without having to invoke a constructor. See here for a CGLib example and here for a ObjenesisHelper example.



         ...
        final Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(data.getClass());
        final Set<Class<?>> interfaces = new LinkedHashSet<>();
        if (data.getClass().isInterface())
        interfaces.add(data.getClass());

        interfaces.addAll(Arrays.asList(data.getClass().getInterfaces()));
        enhancer.setInterfaces(interfaces.toArray(new Class[interfaces.size()]));
        enhancer.setCallbackType(interceptor.getClass());

        final Class<?> proxyClass = enhancer.createClass();
        Enhancer.registerStaticCallbacks(proxyClass, new Callbackinterceptor);
        return (T) ObjenesisHelper.newInstance(proxyClass);




        Caveats



        • This is not a thread safe operation.

        • Reflection will slow down your code.

        • Better error handling needs to added for the reflection calls.

        • If a class does not have a no-arg constructor this will not work.

        • Does not account for inheritance of data classes

        • This could be best effort by checking for a no-arg ctor/setter first.





        share|improve this answer



























          2












          2








          2







          To start off I just want to mention that this probably isn't the best solution and I'm sure there are ways to optimize this. That said, I wanted to try my hand at CGLIB and ObjenesisHelper again.



          Using CGLIB and ObjenesisHelper we can wrap the data object in a proxy which will intercept the get methods. Using this interceptor we can add the logic you described in your post. Lets start off by assume these are our data types (using lombok for brevity).



          @Data class W private X x; 
          @Data class X private Y y;
          @Data class Y private Z z;
          @Data class Z private int alpha;


          Our final solution can be used like the following:



          public static void main(String args) 
          final W w = ProxyUtil.withLazyDefaults(new W());
          System.out.println(w.getX().getY().getZ().getAlpha());



          Implementation



          Currently, if we try to invoke new W().getX().getY().getZ().getAlpha() we will get a NullPointerException when invoking getY() because getX() returned null. Even if we manage to produce a default X value, we will still need a default Y value to not get a null pointer on getZ() and getAlpha() and so forth. The proxy we create needs to be generic and be able to wrap its sub components recursively.



          Okay, so lets start. The first thing we need to do is create a MethodInterceptor. Whenever any call hits our proxy instance it will perform the logic of our MethodInterceptor. We need to first determine if the method called is a getter. If not we will ignore it. During this getter call, if the value is not present in our data we will create it and update the object. If the value contained by the getter is an original unwrapped class, we will replace it with a wraped version. Finally we will return the wrapped instance. Edit I updated this to not inject wrapped instances into the real Data objects. This will be less performant if the object is accessed mutliple times this way



          public class ProxyUtil 
          public static <T> T withLazyDefaults(final T data)
          final MethodInterceptor interceptor = (object, method, args, proxy) ->
          if (method.getName().startsWith("get"))
          final Class<?> returnType = method.getReturnType();
          Object response = method.invoke(data, args);
          if (response == null)
          response = returnType.newInstance();
          data.getClass()
          .getDeclaredMethod(
          method.getName().replaceFirst("get", "set"),
          returnType)
          .invoke(data, response);

          if (!returnType.isPrimitive())
          response = withLazyDefaults(response);

          return response;

          return method.invoke(data, args);
          ;
          ...


          The rest of this method involves using CGLIB and Objenisis Helper to construct the wrapper instance. CGLib will allow you to proxy both classes and interfaces and ObjenesisHelper will allow you to construct an instance of a class without having to invoke a constructor. See here for a CGLib example and here for a ObjenesisHelper example.



           ...
          final Enhancer enhancer = new Enhancer();
          enhancer.setSuperclass(data.getClass());
          final Set<Class<?>> interfaces = new LinkedHashSet<>();
          if (data.getClass().isInterface())
          interfaces.add(data.getClass());

          interfaces.addAll(Arrays.asList(data.getClass().getInterfaces()));
          enhancer.setInterfaces(interfaces.toArray(new Class[interfaces.size()]));
          enhancer.setCallbackType(interceptor.getClass());

          final Class<?> proxyClass = enhancer.createClass();
          Enhancer.registerStaticCallbacks(proxyClass, new Callbackinterceptor);
          return (T) ObjenesisHelper.newInstance(proxyClass);




          Caveats



          • This is not a thread safe operation.

          • Reflection will slow down your code.

          • Better error handling needs to added for the reflection calls.

          • If a class does not have a no-arg constructor this will not work.

          • Does not account for inheritance of data classes

          • This could be best effort by checking for a no-arg ctor/setter first.





          share|improve this answer















          To start off I just want to mention that this probably isn't the best solution and I'm sure there are ways to optimize this. That said, I wanted to try my hand at CGLIB and ObjenesisHelper again.



          Using CGLIB and ObjenesisHelper we can wrap the data object in a proxy which will intercept the get methods. Using this interceptor we can add the logic you described in your post. Lets start off by assume these are our data types (using lombok for brevity).



          @Data class W private X x; 
          @Data class X private Y y;
          @Data class Y private Z z;
          @Data class Z private int alpha;


          Our final solution can be used like the following:



          public static void main(String args) 
          final W w = ProxyUtil.withLazyDefaults(new W());
          System.out.println(w.getX().getY().getZ().getAlpha());



          Implementation



          Currently, if we try to invoke new W().getX().getY().getZ().getAlpha() we will get a NullPointerException when invoking getY() because getX() returned null. Even if we manage to produce a default X value, we will still need a default Y value to not get a null pointer on getZ() and getAlpha() and so forth. The proxy we create needs to be generic and be able to wrap its sub components recursively.



          Okay, so lets start. The first thing we need to do is create a MethodInterceptor. Whenever any call hits our proxy instance it will perform the logic of our MethodInterceptor. We need to first determine if the method called is a getter. If not we will ignore it. During this getter call, if the value is not present in our data we will create it and update the object. If the value contained by the getter is an original unwrapped class, we will replace it with a wraped version. Finally we will return the wrapped instance. Edit I updated this to not inject wrapped instances into the real Data objects. This will be less performant if the object is accessed mutliple times this way



          public class ProxyUtil 
          public static <T> T withLazyDefaults(final T data)
          final MethodInterceptor interceptor = (object, method, args, proxy) ->
          if (method.getName().startsWith("get"))
          final Class<?> returnType = method.getReturnType();
          Object response = method.invoke(data, args);
          if (response == null)
          response = returnType.newInstance();
          data.getClass()
          .getDeclaredMethod(
          method.getName().replaceFirst("get", "set"),
          returnType)
          .invoke(data, response);

          if (!returnType.isPrimitive())
          response = withLazyDefaults(response);

          return response;

          return method.invoke(data, args);
          ;
          ...


          The rest of this method involves using CGLIB and Objenisis Helper to construct the wrapper instance. CGLib will allow you to proxy both classes and interfaces and ObjenesisHelper will allow you to construct an instance of a class without having to invoke a constructor. See here for a CGLib example and here for a ObjenesisHelper example.



           ...
          final Enhancer enhancer = new Enhancer();
          enhancer.setSuperclass(data.getClass());
          final Set<Class<?>> interfaces = new LinkedHashSet<>();
          if (data.getClass().isInterface())
          interfaces.add(data.getClass());

          interfaces.addAll(Arrays.asList(data.getClass().getInterfaces()));
          enhancer.setInterfaces(interfaces.toArray(new Class[interfaces.size()]));
          enhancer.setCallbackType(interceptor.getClass());

          final Class<?> proxyClass = enhancer.createClass();
          Enhancer.registerStaticCallbacks(proxyClass, new Callbackinterceptor);
          return (T) ObjenesisHelper.newInstance(proxyClass);




          Caveats



          • This is not a thread safe operation.

          • Reflection will slow down your code.

          • Better error handling needs to added for the reflection calls.

          • If a class does not have a no-arg constructor this will not work.

          • Does not account for inheritance of data classes

          • This could be best effort by checking for a no-arg ctor/setter first.






          share|improve this answer














          share|improve this answer



          share|improve this answer








          edited Nov 15 '18 at 12:18

























          answered Nov 15 '18 at 9:43









          flakesflakes

          6,90212053




          6,90212053





















              1














              I ended up using a combination of functional and reflection and tried to make the interface similar to Java's Optional. Here is an example of how I would write foo.getX().getY().setZ(val);



              MutableGetter.of(foo).map(Foo::getX).map(x::getY).get().setZ(val);


              This is the code (It's still WIP).
              I used reflection to avoid having to pass the setter and constructor



              import java.lang.reflect.Constructor;
              import java.lang.reflect.Method;
              import java.util.function.BiConsumer;
              import java.util.function.Function;
              import java.util.function.Supplier;

              import javassist.util.proxy.MethodHandler;
              import javassist.util.proxy.ProxyFactory;
              import lombok.Getter;
              import lombok.NonNull;

              public class MutableGetter<T>

              private T object;

              private MutableGetter(T object)

              this.object = object;


              public static <T> MutableGetter<T> of(@NonNull T object)

              return new MutableGetter<>(object);


              public <U> MutableGetter<U> map(Function<T, U> getter)

              Method getterMethod = getGetterMethod(object.getClass(), getter);
              BiConsumer<T, U> setter = getSetter(getterMethod);
              Supplier<U> defaultValue = getDefaultValue(getterMethod);

              U nextObject = getter.apply(object);
              if (nextObject == null)
              nextObject = defaultValue.get();
              setter.accept(object, nextObject);


              return new MutableGetter<>(nextObject);


              public T get()

              return object;


              private static <U> Supplier<U> getDefaultValue(Method getterMethod)

              return () ->
              try
              Constructor<?> constructor = getterMethod.getReturnType().getConstructor();
              constructor.setAccessible(true);
              return (U) constructor.newInstance();
              catch (Exception e)
              throw new IllegalStateException(e);

              ;


              private static <T, U> BiConsumer<T,U> getSetter(Method getterMethod)

              return (obj, arg) ->
              Method setterMethod = getSetterFromGetter(getterMethod);
              setterMethod.setAccessible(true);

              try
              setterMethod.invoke(obj, arg);
              catch (Exception e)
              throw new IllegalStateException(e);

              ;


              private static Method getSetterFromGetter(Method getter)

              if (!getter.getName().startsWith("get"))
              throw new IllegalStateException("The getter method must start with 'get'");


              String setterName = getter.getName().replaceFirst("get", "set");

              Method methods = getter.getDeclaringClass().getMethods();

              for (Method method: methods)
              if (method.getName().equals(setterName))
              return method;



              throw new IllegalStateException(String.format("Couldn't find setter in class %s with name %s", getter.getDeclaringClass(), setterName));


              private static <T, U> Method getGetterMethod(Class<?> clazz, Function<T, U> getter)

              ProxyFactory proxyFactory = new ProxyFactory();
              proxyFactory.setSuperclass(clazz);

              MethodRecorder methodRecorder = new MethodRecorder();

              T proxy;
              try
              proxy = (T) proxyFactory.create(new Class<?>[0], new Object[0], methodRecorder);
              catch (Exception e)
              throw new IllegalStateException(e);


              getter.apply(proxy);

              return methodRecorder.getLastInvokedMethod();


              private static class MethodRecorder implements MethodHandler

              @Getter
              private Method lastInvokedMethod;

              @Override
              public Object invoke(Object self, Method thisMethod, Method proceed, Object args)

              this.lastInvokedMethod = thisMethod;
              return null; // the result is ignored






              Let me know if you have any suggestions






              share|improve this answer



























                1














                I ended up using a combination of functional and reflection and tried to make the interface similar to Java's Optional. Here is an example of how I would write foo.getX().getY().setZ(val);



                MutableGetter.of(foo).map(Foo::getX).map(x::getY).get().setZ(val);


                This is the code (It's still WIP).
                I used reflection to avoid having to pass the setter and constructor



                import java.lang.reflect.Constructor;
                import java.lang.reflect.Method;
                import java.util.function.BiConsumer;
                import java.util.function.Function;
                import java.util.function.Supplier;

                import javassist.util.proxy.MethodHandler;
                import javassist.util.proxy.ProxyFactory;
                import lombok.Getter;
                import lombok.NonNull;

                public class MutableGetter<T>

                private T object;

                private MutableGetter(T object)

                this.object = object;


                public static <T> MutableGetter<T> of(@NonNull T object)

                return new MutableGetter<>(object);


                public <U> MutableGetter<U> map(Function<T, U> getter)

                Method getterMethod = getGetterMethod(object.getClass(), getter);
                BiConsumer<T, U> setter = getSetter(getterMethod);
                Supplier<U> defaultValue = getDefaultValue(getterMethod);

                U nextObject = getter.apply(object);
                if (nextObject == null)
                nextObject = defaultValue.get();
                setter.accept(object, nextObject);


                return new MutableGetter<>(nextObject);


                public T get()

                return object;


                private static <U> Supplier<U> getDefaultValue(Method getterMethod)

                return () ->
                try
                Constructor<?> constructor = getterMethod.getReturnType().getConstructor();
                constructor.setAccessible(true);
                return (U) constructor.newInstance();
                catch (Exception e)
                throw new IllegalStateException(e);

                ;


                private static <T, U> BiConsumer<T,U> getSetter(Method getterMethod)

                return (obj, arg) ->
                Method setterMethod = getSetterFromGetter(getterMethod);
                setterMethod.setAccessible(true);

                try
                setterMethod.invoke(obj, arg);
                catch (Exception e)
                throw new IllegalStateException(e);

                ;


                private static Method getSetterFromGetter(Method getter)

                if (!getter.getName().startsWith("get"))
                throw new IllegalStateException("The getter method must start with 'get'");


                String setterName = getter.getName().replaceFirst("get", "set");

                Method methods = getter.getDeclaringClass().getMethods();

                for (Method method: methods)
                if (method.getName().equals(setterName))
                return method;



                throw new IllegalStateException(String.format("Couldn't find setter in class %s with name %s", getter.getDeclaringClass(), setterName));


                private static <T, U> Method getGetterMethod(Class<?> clazz, Function<T, U> getter)

                ProxyFactory proxyFactory = new ProxyFactory();
                proxyFactory.setSuperclass(clazz);

                MethodRecorder methodRecorder = new MethodRecorder();

                T proxy;
                try
                proxy = (T) proxyFactory.create(new Class<?>[0], new Object[0], methodRecorder);
                catch (Exception e)
                throw new IllegalStateException(e);


                getter.apply(proxy);

                return methodRecorder.getLastInvokedMethod();


                private static class MethodRecorder implements MethodHandler

                @Getter
                private Method lastInvokedMethod;

                @Override
                public Object invoke(Object self, Method thisMethod, Method proceed, Object args)

                this.lastInvokedMethod = thisMethod;
                return null; // the result is ignored






                Let me know if you have any suggestions






                share|improve this answer

























                  1












                  1








                  1







                  I ended up using a combination of functional and reflection and tried to make the interface similar to Java's Optional. Here is an example of how I would write foo.getX().getY().setZ(val);



                  MutableGetter.of(foo).map(Foo::getX).map(x::getY).get().setZ(val);


                  This is the code (It's still WIP).
                  I used reflection to avoid having to pass the setter and constructor



                  import java.lang.reflect.Constructor;
                  import java.lang.reflect.Method;
                  import java.util.function.BiConsumer;
                  import java.util.function.Function;
                  import java.util.function.Supplier;

                  import javassist.util.proxy.MethodHandler;
                  import javassist.util.proxy.ProxyFactory;
                  import lombok.Getter;
                  import lombok.NonNull;

                  public class MutableGetter<T>

                  private T object;

                  private MutableGetter(T object)

                  this.object = object;


                  public static <T> MutableGetter<T> of(@NonNull T object)

                  return new MutableGetter<>(object);


                  public <U> MutableGetter<U> map(Function<T, U> getter)

                  Method getterMethod = getGetterMethod(object.getClass(), getter);
                  BiConsumer<T, U> setter = getSetter(getterMethod);
                  Supplier<U> defaultValue = getDefaultValue(getterMethod);

                  U nextObject = getter.apply(object);
                  if (nextObject == null)
                  nextObject = defaultValue.get();
                  setter.accept(object, nextObject);


                  return new MutableGetter<>(nextObject);


                  public T get()

                  return object;


                  private static <U> Supplier<U> getDefaultValue(Method getterMethod)

                  return () ->
                  try
                  Constructor<?> constructor = getterMethod.getReturnType().getConstructor();
                  constructor.setAccessible(true);
                  return (U) constructor.newInstance();
                  catch (Exception e)
                  throw new IllegalStateException(e);

                  ;


                  private static <T, U> BiConsumer<T,U> getSetter(Method getterMethod)

                  return (obj, arg) ->
                  Method setterMethod = getSetterFromGetter(getterMethod);
                  setterMethod.setAccessible(true);

                  try
                  setterMethod.invoke(obj, arg);
                  catch (Exception e)
                  throw new IllegalStateException(e);

                  ;


                  private static Method getSetterFromGetter(Method getter)

                  if (!getter.getName().startsWith("get"))
                  throw new IllegalStateException("The getter method must start with 'get'");


                  String setterName = getter.getName().replaceFirst("get", "set");

                  Method methods = getter.getDeclaringClass().getMethods();

                  for (Method method: methods)
                  if (method.getName().equals(setterName))
                  return method;



                  throw new IllegalStateException(String.format("Couldn't find setter in class %s with name %s", getter.getDeclaringClass(), setterName));


                  private static <T, U> Method getGetterMethod(Class<?> clazz, Function<T, U> getter)

                  ProxyFactory proxyFactory = new ProxyFactory();
                  proxyFactory.setSuperclass(clazz);

                  MethodRecorder methodRecorder = new MethodRecorder();

                  T proxy;
                  try
                  proxy = (T) proxyFactory.create(new Class<?>[0], new Object[0], methodRecorder);
                  catch (Exception e)
                  throw new IllegalStateException(e);


                  getter.apply(proxy);

                  return methodRecorder.getLastInvokedMethod();


                  private static class MethodRecorder implements MethodHandler

                  @Getter
                  private Method lastInvokedMethod;

                  @Override
                  public Object invoke(Object self, Method thisMethod, Method proceed, Object args)

                  this.lastInvokedMethod = thisMethod;
                  return null; // the result is ignored






                  Let me know if you have any suggestions






                  share|improve this answer













                  I ended up using a combination of functional and reflection and tried to make the interface similar to Java's Optional. Here is an example of how I would write foo.getX().getY().setZ(val);



                  MutableGetter.of(foo).map(Foo::getX).map(x::getY).get().setZ(val);


                  This is the code (It's still WIP).
                  I used reflection to avoid having to pass the setter and constructor



                  import java.lang.reflect.Constructor;
                  import java.lang.reflect.Method;
                  import java.util.function.BiConsumer;
                  import java.util.function.Function;
                  import java.util.function.Supplier;

                  import javassist.util.proxy.MethodHandler;
                  import javassist.util.proxy.ProxyFactory;
                  import lombok.Getter;
                  import lombok.NonNull;

                  public class MutableGetter<T>

                  private T object;

                  private MutableGetter(T object)

                  this.object = object;


                  public static <T> MutableGetter<T> of(@NonNull T object)

                  return new MutableGetter<>(object);


                  public <U> MutableGetter<U> map(Function<T, U> getter)

                  Method getterMethod = getGetterMethod(object.getClass(), getter);
                  BiConsumer<T, U> setter = getSetter(getterMethod);
                  Supplier<U> defaultValue = getDefaultValue(getterMethod);

                  U nextObject = getter.apply(object);
                  if (nextObject == null)
                  nextObject = defaultValue.get();
                  setter.accept(object, nextObject);


                  return new MutableGetter<>(nextObject);


                  public T get()

                  return object;


                  private static <U> Supplier<U> getDefaultValue(Method getterMethod)

                  return () ->
                  try
                  Constructor<?> constructor = getterMethod.getReturnType().getConstructor();
                  constructor.setAccessible(true);
                  return (U) constructor.newInstance();
                  catch (Exception e)
                  throw new IllegalStateException(e);

                  ;


                  private static <T, U> BiConsumer<T,U> getSetter(Method getterMethod)

                  return (obj, arg) ->
                  Method setterMethod = getSetterFromGetter(getterMethod);
                  setterMethod.setAccessible(true);

                  try
                  setterMethod.invoke(obj, arg);
                  catch (Exception e)
                  throw new IllegalStateException(e);

                  ;


                  private static Method getSetterFromGetter(Method getter)

                  if (!getter.getName().startsWith("get"))
                  throw new IllegalStateException("The getter method must start with 'get'");


                  String setterName = getter.getName().replaceFirst("get", "set");

                  Method methods = getter.getDeclaringClass().getMethods();

                  for (Method method: methods)
                  if (method.getName().equals(setterName))
                  return method;



                  throw new IllegalStateException(String.format("Couldn't find setter in class %s with name %s", getter.getDeclaringClass(), setterName));


                  private static <T, U> Method getGetterMethod(Class<?> clazz, Function<T, U> getter)

                  ProxyFactory proxyFactory = new ProxyFactory();
                  proxyFactory.setSuperclass(clazz);

                  MethodRecorder methodRecorder = new MethodRecorder();

                  T proxy;
                  try
                  proxy = (T) proxyFactory.create(new Class<?>[0], new Object[0], methodRecorder);
                  catch (Exception e)
                  throw new IllegalStateException(e);


                  getter.apply(proxy);

                  return methodRecorder.getLastInvokedMethod();


                  private static class MethodRecorder implements MethodHandler

                  @Getter
                  private Method lastInvokedMethod;

                  @Override
                  public Object invoke(Object self, Method thisMethod, Method proceed, Object args)

                  this.lastInvokedMethod = thisMethod;
                  return null; // the result is ignored






                  Let me know if you have any suggestions







                  share|improve this answer












                  share|improve this answer



                  share|improve this answer










                  answered Nov 17 '18 at 6:19









                  Display NameDisplay Name

                  4082515




                  4082515



























                      draft saved

                      draft discarded
















































                      Thanks for contributing an answer to Stack Overflow!


                      • Please be sure to answer the question. Provide details and share your research!

                      But avoid


                      • Asking for help, clarification, or responding to other answers.

                      • Making statements based on opinion; back them up with references or personal experience.

                      To learn more, see our tips on writing great answers.




                      draft saved


                      draft discarded














                      StackExchange.ready(
                      function ()
                      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53315143%2fsafely-call-setter-after-getter-chain-eg-foo-getx-gety-setz%23new-answer', 'question_page');

                      );

                      Post as a guest















                      Required, but never shown





















































                      Required, but never shown














                      Required, but never shown












                      Required, but never shown







                      Required, but never shown

































                      Required, but never shown














                      Required, but never shown












                      Required, but never shown







                      Required, but never shown







                      這個網誌中的熱門文章

                      How to read a connectionString WITH PROVIDER in .NET Core?

                      Node.js Script on GitHub Pages or Amazon S3

                      Museum of Modern and Contemporary Art of Trento and Rovereto