繁体   English   中英

使用 java 中的自定义 object model 构造谓词

[英]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");

我试过一个链接

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

这目前仅适用于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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM