在 java 中使用自定义对象模型构建谓词

Construct a predicate using custom object model in java

我有一个如下所示的对象模型

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");

我试过link

Predicate with Reflection in java

在此,我能够推断出 属性 名称,将 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);
}

这目前仅适用于 Strings,但您可以对其进行调整 - 最简单的方法是删除 return 值的检查,而不是将结果转换为 String 调用.toString()就可以了。

关于动态读取字段的方面只是其中的一部分。这是唯一阻止您的实现完全基于静态代码的因素。

这是一个将 "comparison" 逻辑放入 "operator" 枚举本身的解决方案。

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 将传入对象转换为字符串。

接下来是创建 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 对象转换为谓词:

Filter filter = <dynamic value>;

Predicate<Employee> filterPredicate = filter.getOperator()  
    .toPredicate(fieldExtractor(Employee.class, filter.getField()), 
                 filter.getValue());

如果您认为合适,您甚至可能想要缓存 fieldExtractor 的结果。