K.Nicholas
K.Nicholas

Reputation: 11551

Java Lambda capture or re execute?

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

Upvotes: 2

Views: 111

Answers (3)

Michael
Michael

Reputation: 44210

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.

Upvotes: 0

Andreas
Andreas

Reputation: 159135

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;
}

Upvotes: 2

Silvio Mayolo
Silvio Mayolo

Reputation: 70307

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.

Upvotes: 1

Related Questions