Java Lambda capture or re execute?

Viewed 74

Make a functional interface

@FunctionalInterface
public RulesFact {
    Object getPropertyValue(String propertyName);
}

An instance a lambda inside a method.

public RulesFact rulesFactImpl(Object o) {
    return propertyName->PropertyAccessorFactory.forBeanPropertyAccess(o).getPropertyValue(propertyName);
}

Is there a difference between this method and the above one? In the above one, does the PropertyAccessorFactory.forBeanPropertyAccess method get called each time the lambda is executed? Is there a difference in thread safety?

public RulesFact rulesFactImpl(Object o) {
    BeanWrapper accessor = PropertyAccessorFactory.forBeanPropertyAccess(o);
    return propertyName->accessor.getPropertyValue(propertyName);
}

Javadocs on above reflection utils: Package org.springframework.beans

3 Answers

The lambda expression is executed when RulesFact.getPropertyValue is called.

Which means that the first version calls PropertyAccessorFactory.forBeanPropertyAccess(o) every time RulesFact.getPropertyValue is called, which is good if the accessor can change over time.

The second version calls PropertyAccessorFactory.forBeanPropertyAccess(o) when rulesFactImpl is called, and caches the value, which is good for performance, but bad if the accessor can change over time.

FYI: The second version can be written using method reference:

public RulesFact rulesFactImpl(Object o) {
    BeanWrapper accessor = PropertyAccessorFactory.forBeanPropertyAccess(o);
    return accessor::getPropertyValue;
}

Slightly, but probably not in a way you care about, assuming these APIs are reasonably well-written.

public RulesFact rulesFactImpl(Object o) {
  return propertyName->PropertyAccessorFactory.forBeanPropertyAccess(o).getPropertyValue(propertyName);
}

This returns a RulesFact that, when called, invokes forBeanPropertyAccess and then does whatever work on it. If you call it multiple times, it calls forBeanPropertyAccess each time.

public RulesFact rulesFactImpl(Object o) {
  BeanWrapper accessor = PropertyAccessorFactory.forBeanPropertyAccess(o);
  return propertyName->accessor.getPropertyValue(propertyName);
}

This calls forBeanPropertyAccess once and makes a closure around its value. You can think of this closure as being like an instance variable. We're, in some sense, making a subclass of RulesFact which has an instance variable called accessor whose value is computed at construction time only once. If we call this lambda several times, forBeanPropertyAccess will never get called again; it'll just use the original value several times.

It's more or less the same but it's not functionally identical. Suppose PropertyAccessorFactory.forBeanPropertyAccess(o) throws an exception.

In the first case, rulesFactImpl(...) would never throw. Provided RulesFact.getPropertyValue was never called, there would be no exceptions.

In the second case,rulesFactImpl(...) would always throw.

Related