[英]Construct a predicate using custom object model in java
我有一个 object model 如下所示
public class Filter {
public String field;
public ConditionalOperator operator;
public String value;
}
我有一个对象列表,例如List<Employees>
基于过滤器输入,我想在属性上构造谓词并将其应用于员工列表。
例子:
Employees
FirstName
LastName
CreatedOn (Timestamp)
Status (FullTime/ parttime)
IsActive(True / False)
Filter conditions will be looking like
[
{ "field":"FirstName", "operator":"StartsWith", "value":"john"}
]
运营商就像
Contains
,StartsWith
,EndsWith
,Equals
我想构造像PredicateBuilder(fieldName, operator, value)
这样的谓词,这样我就可以得到
Predicate<Employees> filter = e -> e.FirstName.StartsWith("john");
我试过一个链接
在此,我能够推断出属性名称,将 equals 方法应用于动态值,例如
Class<?> cls = Employees.class;
Class<?> noparams[] = {};
try {
Method method = cls.getDeclaredMethod("get" + filter.getField(), noparams);
Predicate<ExecutionReport> first = e -> {
try {
Object val = method.invoke(e);
System.out.println(val);
return method.invoke(e).toString().startsWith(filter.getField());
} catch (IllegalAccessException illegalAccessException) {
illegalAccessException.printStackTrace();
} catch (InvocationTargetException invocationTargetException) {
invocationTargetException.printStackTrace();
}
return false;
};
return first;
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
请指导我如何构建动态谓词,我一直在搜索互联网,但没有运气,我在 Java 中关于反射和谓词的信息较少
让我们从使用一些简单的构建块开始:
public enum ConditionalOperator implements BiPredicate<String, String> {
Contains((test, value) -> test.contains(value)),
StartsWith((test, value) -> test.startsWith(value)),
EndsWith((test, value) -> test.endsWith(value)),
Equals((test, value) -> test.equals(value));
private final BiPredicate<String, String> predicate;
ConditionalOperator(BiPredicate<String, String> predicate) {
this.predicate = predicate;
}
@Override
public boolean test(String test, String value) {
return predicate.test(test, value);
}
}
我冒昧地将它实现为enum
,不确定它在您的设计中是什么。
现在我们需要一个值提取器:
public static Function<Employee, String> getField(String name) {
try {
Method method = Employee.class.getMethod("get" + name);
if (method.getReturnType() != String.class) {
throw new IllegalArgumentException("Employee.get" + name + " does not return a String");
}
return e -> {
try {
return (String) method.invoke(e);
} catch (ReflectiveOperationException roe) {
// Unlikely to happen
throw new RuntimeException(roe);
}
}
} catch (ReflectiveOperationException roe) {
// getter does not exist, what now?
throw new IllegalArgumentException(roe);
}
}
最后,我们需要将所有内容链接在一起:
public static Predicate<Employee> buildPredicate(Filter f) {
Function<Employee, String> fieldGetter = getField(f.field());
ConditionalOperator op = f.operator();
String value = f.value();
return e -> op.test(fieldGetter.apply(e), value);
}
这目前仅适用于String
,但您可能可以对其进行调整 - 最简单的方法是删除对返回值的检查,而不是将结果转换为 String 调用.toString()
。
动态读取字段的方面只是其中的一部分。 这是阻止您的实现完全基于 static 代码的唯一原因。
这是一个将“比较”逻辑放入“运算符”枚举本身的解决方案。
enum ConditionalOperator {
CONTAINS(String::contains),
STARTSWITH(String::startsWith),
ENDSWITH(String::endsWith),
EQUALS(String::equals);
private final BiPredicate<String, String> predicate;
private ConditionalOperator(BiPredicate<String, String> predicate) {
this.predicate = predicate;
}
public <T> Predicate<T> toPredicate(Function<T, String> getter,
String search) {
return object -> this.predicate.test(getter.apply(object), search);
}
}
toPredicate()
方法采用一个 getter,它将传入的 object 转换为字符串。
接下来是一个 function 方法,它在给定类型和字段名称的情况下创建 getter:
private static <T> Function<T, String> fieldExtractor(Class<T> cls, String field){
return object -> {
try {
Field f = cls.getDeclaredField(field);
f.setAccessible(true);
return (String) f.get(object);
} catch (Exception e) {
//handle properly
throw new RuntimeException(e);
}
};
}
使用上述内容,您可以通过执行以下操作将Filter
object 转换为谓词:
Filter filter = <dynamic value>;
Predicate<Employee> filterPredicate = filter.getOperator()
.toPredicate(fieldExtractor(Employee.class, filter.getField()),
filter.getValue());
如果您认为合适,您甚至可能想要缓存fieldExtractor
的结果。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.