I have a stream of BusinessObjects, 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");
Not entirely sure I get what you might need, but something like this?
static Function<BusinessObject, Function<SomeDiscriminator, Function<String, BusinessObject>>>
setNewValueInBusinessObjectWithFun =
x -> y -> z -> {
if ("firstfield".equals(y.value)) {
x.firstField = z;
} else {
x.secondField = z;
}
return x;
};
And usage would be:
BusinessObject bo = new BusinessObject();
bo.firstField = "test";
SomeDiscriminator sd = new SomeDiscriminator();
sd.value = "firstField";
bo = setNewValueInBusinessObjectWithFun.apply(bo).apply(sd).apply("value");
System.out.println(bo.firstField);
This doesn't need to be complex. If you have a Stream<BusinessObject>, then neither SomeDiscriminator nor the String value is a stream element.
So it's a Function<BusinessObject, BusinessObject> that you need to pass to map:
Stream<BusinessObject> businessObjectStream = null;
SomeDiscriminator discriminator = null; String newValue = "";
Function<BusinessObject, BusinessObject> mapper = businessObject -> {
if (discriminator.value.equals("firstField")) {
businessObject.firstField = newValue;
} else {//secondField
businessObject.secondField = newValue;
}
return businessObject;
};
Called with:
businessObjectStream.map(mapper);
Even if the discriminator.value.equals("firstField") logic were dependent on dynamic values in the stream object, you would have needed perhaps a Predicate<...> somewhere, but really not even a higher-order function that returns a function dynamically.
Related
Trying to refactor the code. Now the code is:
if ("objects".equals(type)) {
Object oldJson = oldData.get("content");
Object newJson = newData.get("content");
} else if ("objects.appeals".equals(type)) {
Object oldJson = oldData.get("data").get("person");
Object newJson = newData.get("data").get("person");
}
The number of types is much larger. I gave only 2 for an example. Trying to optimize with enum:
public enum HistoryUpdateTypeEnum {
OBJECTS("objects", new Document()),
APPEALS_OBJECTS("appeals.objects", new Document());
HistoryUpdateTypeEnum(String type, Document documentSlice) {
this.type = type;
this.documentSlice = documentSlice;
}
private String type;
private Document documentSlice;
public static HistoryUpdateTypeEnum fromString(String value) {
return Stream.of(values())
.filter(Objects::nonNull)
.filter(v -> v.name().replaceAll("_",".").equalsIgnoreCase(value))
.findAny()
.orElse(null);
}
public Object formSlice(Document data) {
this.documentSlice = data;
return documentSlice.get("content"); // How to make it universal?
}
}
And use:
HistoryUpdateTypeEnum typeEnum = HistoryUpdateTypeEnum.fromString("objects.appeals");
Document oldData = new Document(......).append(..., ...);
Document newData = new Document(......).append(..., ...);
Object oldJson = typeEnum.formSlice(oldData);
Object newJson = typeEnum.formSlice(newData);
I can’t figure out how to make me perform my action for each type. That is, documentSlice.get ("content") for 'objects' or documentSlice.get("data").get("person") for 'appeals.objects'. Are there any ideas?
One of the possible variants is abstract method in your Enum class:
public enum HistoryUpdateTypeEnum {
OBJECTS {
#Override
Object getJson(Document data) {
return data.get("objects");
}
},
...
abstract Object getJson(Document data);
}
Then you could use it in such way:
HistoryUpdateTypeEnum history = HistoryUpdateTypeEnum .valueOf(type.toUpperCase());
Object oldJson = history.getJson(oldData);
Object newJson = history.getJson(newData);
Using Java 8 I have a Map<String, String> and I want to use a method of one of the standard libraries to return a Map with a specified entry removed.
So essentially I'm looking for a way to achieve just by a function call or by function concatenation the following (hypothetical) method of Map:
Map<String, String> removed(String key) {
this.remove(key);
return this;
}
The whole thing would help me to convert a Map of a Bean into a pretty string in one line
The version that does not modify the input map could look like this.
return map.entrySet().stream()
.filter(e -> !e.getKey().equals(other))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
Yes, I do not think it can get easier than that.
If all you want to do, is to implement a toString() method skipping the "class" property, you could do that straight-forwardly without manipulating the Map:
#Override
public String toString() {
return BeanUtils.describe(this).entrySet().stream()
.filter(e -> !e.getKey().equals("class"))
.map(Object::toString)
.collect(Collectors.joining("\n", super.toString()+":\n", ""));
}
But to me, not being able to exclude the class property in the first place, looks like an unfortunate omission. Consider:
public class MyBeanUtils {
public static Map<String,Object> describe(Object o) {
try {
return describe(o, Introspector.getBeanInfo(o.getClass()));
} catch (IntrospectionException ex) {
throw new IllegalStateException(ex);
}
}
public static Map<String,Object> describe(Object o, Class<?> stopClass) {
try {
return describe(o, Introspector.getBeanInfo(o.getClass(), stopClass));
} catch (IntrospectionException ex) {
throw new IllegalStateException(ex);
}
}
private static Map<String,Object> describe(Object o, BeanInfo beanInfo) {
Map<String,Object> map=new HashMap<>();
for(PropertyDescriptor fd: beanInfo.getPropertyDescriptors()) {
if(fd.getReadMethod()!=null) try {
map.put(fd.getName(), fd.getReadMethod().invoke(o));
} catch(IllegalAccessException | InvocationTargetException ex){}
}
return map;
}
}
Now, by specifying Object.class as stop class, we exclude its properties, which is exactly the class property defined by Object.getClass():
#Override
public String toString() {
return MyBeanUtils.describe(this, Object.class).entrySet().stream()
.map(Object::toString)
.collect(Collectors.joining("\n", super.toString()+":\n", ""));
}
This even enables another opportunity. If we assume that super.toString() already cares for all properties of the super class hierarchy, we can specify our own superclass as stop class to only add properties defined in our class:
public class BeanExample extends JButton {
String foo = "some string";
int bar = 42;
public String getFoo() {
return foo;
}
public void setFoo(String foo) {
this.foo = foo;
}
public int getBar() {
return bar;
}
public void setBar(int bar) {
this.bar = bar;
}
#Override
public String toString() {
return MyBeanUtils.describe(this, getClass().getSuperclass()).entrySet().stream()
.map(Object::toString)
.collect(Collectors.joining("\n", super.toString()+":\n", ""));
}
}
→
BeanExample[,0,0,0x0,invalid,alignmentX=0.0,alignmentY=0.5,border=javax.swing.plaf.BorderUIResource$CompoundBorderUIResource#b1bc7ed,flags=296,maximumSize=,minimumSize=,preferredSize=,defaultIcon=,disabledIcon=,disabledSelectedIcon=,margin=javax.swing.plaf.InsetsUIResource[top=2,left=14,bottom=2,right=14],paintBorder=true,paintFocus=true,pressedIcon=,rolloverEnabled=true,rolloverIcon=,rolloverSelectedIcon=,selectedIcon=,text=,defaultCapable=true]:
bar=42
foo=some string
Suppose there is a simple enum called Type defined like this:
enum Type{
X("S1"),
Y("S2");
private String s;
private Type(String s) {
this.s = s;
}
}
Finding the correct enum for given s is trivially done with static method with for-loop (assume the method is defined inside enum), e.g.:
private static Type find(String val) {
for (Type e : Type.values()) {
if (e.s.equals(val))
return e;
}
throw new IllegalStateException(String.format("Unsupported type %s.", val));
}
I think the functional equivalent of this expressed with Stream API would be something like this:
private static Type find(String val) {
return Arrays.stream(Type.values())
.filter(e -> e.s.equals(val))
.reduce((t1, t2) -> t1)
.orElseThrow(() -> {throw new IllegalStateException(String.format("Unsupported type %s.", val));});
}
How could we write this better and simpler? This code feels coerced and not very clear. The reduce() especially seems clunky and abused as it doesn't accumulate anything, performs no calculation and always simply returns t1 (provided the filter returns one value - if it doesn't that's clearly a disaster), not to mention t2 is there superfluous and confusing. Yet I couldn't find anything in Stream API that simply somehow returns directly a T from a Stream<T>.
Is there a better way?
I would use findFirst instead:
return Arrays.stream(Type.values())
.filter(e -> e.s.equals(val))
.findFirst()
.orElseThrow(() -> new IllegalStateException(String.format("Unsupported type %s.", val)));
Though a Map could be better in this case:
enum Type{
X("S1"),
Y("S2");
private static class Holder {
static Map<String, Type> MAP = new HashMap<>();
}
private Type(String s) {
Holder.MAP.put(s, this);
}
public static Type find(String val) {
Type t = Holder.MAP.get(val);
if(t == null) {
throw new IllegalStateException(String.format("Unsupported type %s.", val));
}
return t;
}
}
I learnt this trick from this answer. Basically the class loader initializes the static classes before the enum class, which allows you to fill the Map in the enum constructor itself. Very handy !
Hope it helps ! :)
The accepted answer works well, but if you want to avoid creating a new stream with a temporary array you could use EnumSet.allOf().
EnumSet.allOf(Type.class)
.stream()
.filter(e -> e.s.equals(val))
.findFirst()
.orElseThrow(() -> new IllegalStateException(String.format("Unsupported type %s.", val)));
Arrays.stream(Type.values()).filter(v -> v.s.equals(val)).findAny().orElseThrow(...);
How about using findAny() instead of reduce?
private static Type find(String val) {
return Arrays.stream(Type.values())
.filter(e -> e.s.equals(val))
.findAny()
.orElseThrow(() -> new IllegalStateException(String.format("Unsupported type %s.", val)));
}
I think the second answer of Alexis C. (Alexis C.'s answer) is the good one in term of complexity. Instead of searching in O(n) each time you look for a code using
return Arrays.stream(Type.values())
.filter(e -> e.s.equals(val))
.findFirst()
.orElseThrow(() -> new IllegalStateException(String.format("Unsupported type %s.", val)));
you could use O(n) time at the loading of the class by putting all elements into the map, and then access to the code of the type in constant time O(1) using the map.
enum Type{
X("S1"),
Y("S2");
private final String code;
private static Map<String, Type> mapping = new HashMap<>();
static {
Arrays.stream(Type.values()).forEach(type-> mapping.put(type.getCode(), type));
}
Type(String code) {
this.code = code;
}
public String getCode() {
return code;
}
public static Type forCode(final String code) {
return mapping.get(code);
}
}
I know this question is old but I came here from a duplicate. My answer is not strictly answering the OP's question about how to solve the problem using Java Streams. Instead, this answer expands the Map-based solution proposed in the accepted answer to become more (IMHO) manageable.
So here it is: I propose to introduce a special helper class that I named EnumLookup.
Assuming the Type enumeration is slightly better written (meaningful field name + getter), I inject an EnumLookup constant to it like below:
enum Type {
X("S1"),
Y("S2");
private static final EnumLookup<Type, String> BY_CODE = EnumLookup.of(Type.class, Type::getCode, "code");
private final String code;
Type(String code) {
this.code = code;
}
public String getCode() {
return code;
}
public static EnumLookup<Type, String> byCode() {
return BY_CODE;
}
}
The usage then becomes (again, IMO) really readable:
Type type = Type.byCode().get("S1"); // returns Type.X
Optional<Type> optionalType = Type.byCode().find("S2"); // returns Optional(Type.Y)
if (Type.byCode().contains("S3")) { // returns false
// logic
}
Finally, here's the code of the EnumLookup helper class:
public final class EnumLookup<E extends Enum<E>, ID> {
private final Class<E> enumClass;
private final ImmutableMap<ID, E> valueByIdMap;
private final String idTypeName;
private EnumLookup(Class<E> enumClass, ImmutableMap<ID, E> valueByIdMap, String idTypeName) {
this.enumClass = enumClass;
this.valueByIdMap = valueByIdMap;
this.idTypeName = idTypeName;
}
public boolean contains(ID id) {
return valueByIdMap.containsKey(id);
}
public E get(ID id) {
E value = valueByIdMap.get(id);
if (value == null) {
throw new IllegalArgumentException(String.format(
"No such %s with %s: %s", enumClass.getSimpleName(), idTypeName, id
));
}
return value;
}
public Optional<E> find(ID id) {
return Optional.ofNullable(valueByIdMap.get(id));
}
//region CONSTRUCTION
public static <E extends Enum<E>, ID> EnumLookup<E, ID> of(
Class<E> enumClass, Function<E, ID> idExtractor, String idTypeName) {
ImmutableMap<ID, E> valueByIdMap = Arrays.stream(enumClass.getEnumConstants())
.collect(ImmutableMap.toImmutableMap(idExtractor, Function.identity()));
return new EnumLookup<>(enumClass, valueByIdMap, idTypeName);
}
public static <E extends Enum<E>> EnumLookup<E, String> byName(Class<E> enumClass) {
return of(enumClass, Enum::name, "enum name");
}
//endregion
}
Note that:
I used Guava's ImmutableMap here, but a regular HashMap or LinkedHashMap can be used instead.
If you mind the lack of lazy initialization in the above approach, you can delay building of the EnumLookup until byCode method is first called (e.g. using the lazy-holder idiom, like in the accepted answer)
You'd need a getter for String s, but this is the pattern I use:
private static final Map<String, Type> TYPE_MAP =
Collections.unmodifiableMap(
EnumSet.allOf(Type.class)
.stream()
.collect(Collectors.toMap(Type::getS, e -> e)));
public static Type find(String s) {
return TYPE_MAP.get(s);
}
No for loops, only streams. Quick lookup as opposed to building a stream every time the method is called.
I can't add a comment yet, so I am posting an answer to complement the above answer, just following the same idea but using java 8 approach:
public static Type find(String val) {
return Optional
.ofNullable(Holder.MAP.get(val))
.orElseThrow(() -> new IllegalStateException(String.format("Unsupported type %s.", val)));
}
You need a getter for String s.
In the example below this method is getDesc():
public static StatusManifestoType getFromValue(String value) {
return Arrays.asList(values()).stream().filter(t -> t.getDesc().equals(value)).findAny().orElse(null);
}
Suppose there is a simple enum called Type defined like this:
enum Type{
X("S1"),
Y("S2");
private String s;
private Type(String s) {
this.s = s;
}
}
Finding the correct enum for given s is trivially done with static method with for-loop (assume the method is defined inside enum), e.g.:
private static Type find(String val) {
for (Type e : Type.values()) {
if (e.s.equals(val))
return e;
}
throw new IllegalStateException(String.format("Unsupported type %s.", val));
}
I think the functional equivalent of this expressed with Stream API would be something like this:
private static Type find(String val) {
return Arrays.stream(Type.values())
.filter(e -> e.s.equals(val))
.reduce((t1, t2) -> t1)
.orElseThrow(() -> {throw new IllegalStateException(String.format("Unsupported type %s.", val));});
}
How could we write this better and simpler? This code feels coerced and not very clear. The reduce() especially seems clunky and abused as it doesn't accumulate anything, performs no calculation and always simply returns t1 (provided the filter returns one value - if it doesn't that's clearly a disaster), not to mention t2 is there superfluous and confusing. Yet I couldn't find anything in Stream API that simply somehow returns directly a T from a Stream<T>.
Is there a better way?
I would use findFirst instead:
return Arrays.stream(Type.values())
.filter(e -> e.s.equals(val))
.findFirst()
.orElseThrow(() -> new IllegalStateException(String.format("Unsupported type %s.", val)));
Though a Map could be better in this case:
enum Type{
X("S1"),
Y("S2");
private static class Holder {
static Map<String, Type> MAP = new HashMap<>();
}
private Type(String s) {
Holder.MAP.put(s, this);
}
public static Type find(String val) {
Type t = Holder.MAP.get(val);
if(t == null) {
throw new IllegalStateException(String.format("Unsupported type %s.", val));
}
return t;
}
}
I learnt this trick from this answer. Basically the class loader initializes the static classes before the enum class, which allows you to fill the Map in the enum constructor itself. Very handy !
Hope it helps ! :)
The accepted answer works well, but if you want to avoid creating a new stream with a temporary array you could use EnumSet.allOf().
EnumSet.allOf(Type.class)
.stream()
.filter(e -> e.s.equals(val))
.findFirst()
.orElseThrow(() -> new IllegalStateException(String.format("Unsupported type %s.", val)));
Arrays.stream(Type.values()).filter(v -> v.s.equals(val)).findAny().orElseThrow(...);
How about using findAny() instead of reduce?
private static Type find(String val) {
return Arrays.stream(Type.values())
.filter(e -> e.s.equals(val))
.findAny()
.orElseThrow(() -> new IllegalStateException(String.format("Unsupported type %s.", val)));
}
I think the second answer of Alexis C. (Alexis C.'s answer) is the good one in term of complexity. Instead of searching in O(n) each time you look for a code using
return Arrays.stream(Type.values())
.filter(e -> e.s.equals(val))
.findFirst()
.orElseThrow(() -> new IllegalStateException(String.format("Unsupported type %s.", val)));
you could use O(n) time at the loading of the class by putting all elements into the map, and then access to the code of the type in constant time O(1) using the map.
enum Type{
X("S1"),
Y("S2");
private final String code;
private static Map<String, Type> mapping = new HashMap<>();
static {
Arrays.stream(Type.values()).forEach(type-> mapping.put(type.getCode(), type));
}
Type(String code) {
this.code = code;
}
public String getCode() {
return code;
}
public static Type forCode(final String code) {
return mapping.get(code);
}
}
I know this question is old but I came here from a duplicate. My answer is not strictly answering the OP's question about how to solve the problem using Java Streams. Instead, this answer expands the Map-based solution proposed in the accepted answer to become more (IMHO) manageable.
So here it is: I propose to introduce a special helper class that I named EnumLookup.
Assuming the Type enumeration is slightly better written (meaningful field name + getter), I inject an EnumLookup constant to it like below:
enum Type {
X("S1"),
Y("S2");
private static final EnumLookup<Type, String> BY_CODE = EnumLookup.of(Type.class, Type::getCode, "code");
private final String code;
Type(String code) {
this.code = code;
}
public String getCode() {
return code;
}
public static EnumLookup<Type, String> byCode() {
return BY_CODE;
}
}
The usage then becomes (again, IMO) really readable:
Type type = Type.byCode().get("S1"); // returns Type.X
Optional<Type> optionalType = Type.byCode().find("S2"); // returns Optional(Type.Y)
if (Type.byCode().contains("S3")) { // returns false
// logic
}
Finally, here's the code of the EnumLookup helper class:
public final class EnumLookup<E extends Enum<E>, ID> {
private final Class<E> enumClass;
private final ImmutableMap<ID, E> valueByIdMap;
private final String idTypeName;
private EnumLookup(Class<E> enumClass, ImmutableMap<ID, E> valueByIdMap, String idTypeName) {
this.enumClass = enumClass;
this.valueByIdMap = valueByIdMap;
this.idTypeName = idTypeName;
}
public boolean contains(ID id) {
return valueByIdMap.containsKey(id);
}
public E get(ID id) {
E value = valueByIdMap.get(id);
if (value == null) {
throw new IllegalArgumentException(String.format(
"No such %s with %s: %s", enumClass.getSimpleName(), idTypeName, id
));
}
return value;
}
public Optional<E> find(ID id) {
return Optional.ofNullable(valueByIdMap.get(id));
}
//region CONSTRUCTION
public static <E extends Enum<E>, ID> EnumLookup<E, ID> of(
Class<E> enumClass, Function<E, ID> idExtractor, String idTypeName) {
ImmutableMap<ID, E> valueByIdMap = Arrays.stream(enumClass.getEnumConstants())
.collect(ImmutableMap.toImmutableMap(idExtractor, Function.identity()));
return new EnumLookup<>(enumClass, valueByIdMap, idTypeName);
}
public static <E extends Enum<E>> EnumLookup<E, String> byName(Class<E> enumClass) {
return of(enumClass, Enum::name, "enum name");
}
//endregion
}
Note that:
I used Guava's ImmutableMap here, but a regular HashMap or LinkedHashMap can be used instead.
If you mind the lack of lazy initialization in the above approach, you can delay building of the EnumLookup until byCode method is first called (e.g. using the lazy-holder idiom, like in the accepted answer)
You'd need a getter for String s, but this is the pattern I use:
private static final Map<String, Type> TYPE_MAP =
Collections.unmodifiableMap(
EnumSet.allOf(Type.class)
.stream()
.collect(Collectors.toMap(Type::getS, e -> e)));
public static Type find(String s) {
return TYPE_MAP.get(s);
}
No for loops, only streams. Quick lookup as opposed to building a stream every time the method is called.
I can't add a comment yet, so I am posting an answer to complement the above answer, just following the same idea but using java 8 approach:
public static Type find(String val) {
return Optional
.ofNullable(Holder.MAP.get(val))
.orElseThrow(() -> new IllegalStateException(String.format("Unsupported type %s.", val)));
}
You need a getter for String s.
In the example below this method is getDesc():
public static StatusManifestoType getFromValue(String value) {
return Arrays.asList(values()).stream().filter(t -> t.getDesc().equals(value)).findAny().orElse(null);
}
I want to implement Database systems in functionality by using the predicate.
This is as like if SQL filter a recordset by in it cumbersome the results.
But if i pass the List as in predicate it takes only one value i.e. if i am passing 53 and 54 it filter the results for 53 only.
public class classNamePredicate implements Predicate<className> {
private Object expected1;
private String property;
private List<Object> listOfValues = new ArrayList<Object>();
public SalesOrderPredicate(Object expected1, String property) {
super();
this.expected1 = expected1;
this.property = property;
}
public SalesOrderPredicate(List<Object> listValues, String property) {
this.listOfValues = listValues;
this.property = property;
}
#Override
public boolean evaluate(SalesOrder object) {
try {
if (property.equals("volume")) {
return ((Integer) expected1 < object.getVolume());
}
if (property.equals("startDateId")) {
return (expected1.equals(object.getStartDateId()));
}
if (property.equals("endDateId")) {
return (expected1.equals(object.getEndDateId()));
}
if (property.equals("productIds")) {
for (Object value : listOfValues) {
return (object.getProductId() == (Integer) value);
}
}
if (property.equals("sourceIds")) {
for (Object value : listOfValues) {
return (object.getSourceId() == (Integer) value);
}
}
return false;
} catch (Exception e) {
return false;
}
}
}
I am trying to use this as per the following way:
List<Object> productIds = new ArrayList<Object>();
productIds.add(53);
productIds.add(54);
List<Object> sourceIds = new ArrayList<Object>();
sourceIds.add(122);
Predicate[] classnameOrderPredicate = { (Predicate) new classnamePredicate(4415, "startDateId"),
(Predicate) new classnamePredicate(4443, "endDateId"), (Predicate) new classnamePredicate(100000, "volume"),
(Predicate) new classnamePredicate(productIds, "productIds"), (Predicate) new classnamePredicate(sourceIds, "sourceIds") };
Predicate classnameallPredicateGeneric = (Predicate) PredicateUtils
.allPredicate((org.apache.commons.collections4.Predicate<? super classname>[]) classnamePredicate);
Collection<classname> classnamefilteredCollectionGeneric = GenericCollectionUtils.select(classname, classnameallPredicateGeneric);
Please suggest in design perspective too.
Thanks in advance
You're only evaluating the first item in the collection:
for (Object value : listOfValues) {
return (object.getProductId() == (Integer) value);
}
You want to evaluate all of them, and Java conveniently provides a contains() method for that:
return listOfValues.contains(object.getProductId());
Other than that, the code looks pretty awful, you should create smaller, targeted Predicates, instead of writing a generic one with lots of different cases. You could get rid of those casts at the same time.
You also failed at your obfuscation by failing to replace a few SalesOrder by className (which doesn't respect the Java coding standard and is distracting).