I have a stream of BusinessObject
s, I need to set a value on each object, I want to use Stream.map
but map
takes a Function<T,R>
and I have the target object, a discriminator value, and the new value. setNewValueInBusinessObjectExample
shows what I want to do and setNewValueInBusinessObjectWithFun
is what I need help with.
Agreed! I can just use setNewValueInBusinessObjectExample
on the map but I want to see how the functional style looks like. Thanks
class BusinessObject {
String firstField;
String secondField;
}
class SomeDiscriminator {
String value;
}
BusinessObject setNewValueInBusinessObjectExample(BusinessObject businessObject,
SomeDiscriminator discriminator, String newValue) {
if(discriminator.value.equals("firstField")) {
businessObject.firstField = newValue;
} else {//secondField
businessObject.secondField = newValue;
}
return businessObject;
}
Function<BusinessObject, Function<SomeDiscriminator, Function<String, BusinessObject>>>
setNewValueInBusinessObjectWithFun = {
/* todo: using nested Function<T,R> */
}
If you are in doubt with the construction and usage of functional interfaces, I recommend you to expand the whole thing to anonymous classes where the structure becomes obvious.
I also noticed the whole flow uses three parameters, the same your setNewValueInBusinessObjectExample
does. Thus move the body of the method to the innermost anonymous class.
Function<BusinessObject, Function<SomeDiscriminator, Function<String, BusinessObject>>> setNewValueInBusinessObjectWithFun =
new Function<BusinessObject, Function<SomeDiscriminator, Function<String, BusinessObject>>>() {
@Override
public Function<SomeDiscriminator, Function<String, BusinessObject>> apply(final BusinessObject businessObject) {
return new Function<SomeDiscriminator, Function<String, BusinessObject>>() {
@Override
public Function<String, BusinessObject> apply(final SomeDiscriminator someDiscriminator) {
return new Function<String, BusinessObject>() {
@Override
public BusinessObject apply(final String newValue) {
if (someDiscriminator.value.equals("firstField")) {
businessObject.firstField = newValue;
} else {//secondField
businessObject.secondField = newValue;
}
return businessObject;
}
};
}
};
}
};
Now, pack the whole thing to lambda expressions and see what happens:
Function<BusinessObject, Function<SomeDiscriminator, Function<String, BusinessObject>>> setNewValueInBusinessObjectWithFun =
businessObject -> someDiscriminator -> newValue -> {
if (someDiscriminator.value.equals("firstField")) {
businessObject.firstField = newValue;
} else {//secondField
businessObject.secondField = newValue;
}
return businessObject;
};
For sake of clarity, name the variables inside the lambdas expression correctly, otherwise you would not be able to work with them well. The usage is fairly simple (I moved setters to constructor for sake of brevity:
BusinessObject businessObject = new BusinessObject("oldValue");
setNewValueInBusinessObjectWithFun
.apply(businessObject) // apply to an object
.apply(new SomeDiscriminator("firstField")) // finds its field
.apply("newValue"); // sets a new value
However, I recommend you to define a custom @FunctionalInterface
with more straightforward definition...
@FunctionalInterface
interface MyFunction<T, R, U> {
T apply(T t, R r, U u);
}
... and usage ...
MyFunction<BusinessObject, SomeDiscriminator, String> myFunction =
(businessObject, someDiscriminator, newValue) -> {
if (someDiscriminator.value.equals("firstField")) {
businessObject.firstField = newValue;
} else {
businessObject.secondField = newValue;
}
return businessObject;
};
BusinessObject businessObject = new BusinessObject("oldValue");
myFunction.apply(businessObject, new SomeDiscriminator("firstField"), "newValue");