java - 调用Comparator比较方法中的方法

标签 java reflection comparator

我有一个名为 tasks 的 ArrayList<Task> ,我想要打印它,根据 Task 对象的每个字段进行排序。 任务类具有三个私有(private)字段 { String title, Date date, String project }。 该类有一些公共(public) get 方法,允许其他类读取字段 { getTitle(), getDate(), getProject(), <强>getTaskDetails() }.

我有一个简单的方法,使用流来排序和打印 ArryaList 任务:

tasks.stream()
     .sorted(Comparator.comparing(Task::getProject))
     .map(Task::getTaskDetails)
     .forEach(System.out::println);

为了根据每个不同的 getter 方法进行排序,我不想创建 3 种不同的方法,而是使用 Reflection API。但我在调用 Comparator 比较方法中的方法( getTitle()getDate()getProject() )时遇到问题:

使用导入java.lang.reflect.Method;

声明方法Method taskMethod = Task.class.getDeclaredMethod(methodName);其中methodName将是与方法名称一起接收的参数字符串(“getTitle ”“getDate”“getProject”)。

然后尝试做这样的事情,但没有锻炼:

            tasks.stream()
                    .sorted(Comparator.comparing(task -> {
                                try {
                                    taskMethod.invoke(task);
                                } catch (Exception e) {
                                    e.printStackTrace();
                                }
                            }))
                    .map(Task::getTaskDetails)
                    .forEach(System.out::println);

这可能吗?或者有更简单的解决方案吗?

只找到这个question但没有解决我的问题。

感谢您的任何反馈。

最佳答案

您链接到的答案基本上包含核心思想,即使它指的是字段(“属性”)而不是方法:创建一种从对象获取所需值的方法,然后简单地比较这些值两个物体。

有人可以将其视为重复项,但我不确定。

无论如何:

您应该仔细考虑反射是否是正确的方法。

在没有反射的情况下生成所需的比较器可能会更优雅(即更少黑客)。这可以是一个枚举,您可以将正确的 Comparator 附加到每个枚举值:

enum TaskProperty {
    TITLE(comparatorForTitle), 
    DATE(comparatorForDate), ...
}

// Using it:
tasks.stream().sorted(TaskProperty.TITLE.getComparator()).forEach(...);

或者也许(类型安全性较差,但更灵活),使用可以通过字符串查找它们的 map ,如

// Put all comparators into a map
map.put("getDate", compareTaskByDate);
...
// Later, use the string to pick up the comparator from the map:
tasks.stream().sorted(map.get("getDate")).forEach(...);
<小时/>

如果您仔细考虑过这一点,并且确实想要使用基于反射的方法:

您可以创建一个实用方法来获取给定对象的特定方法的返回值。然后创建一个比较器,为给定对象调用此方法,并比较返回值。为了灵 active ,您可以将返回值传递给下游比较器(默认情况下可以是 naturalOrder)。

此处显示了如何完成此操作的示例。但是 getOptional 中捕获的异常列表应该清楚地表明,这里可能会出现很多问题,您应该真正考虑采用不同的方法:

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

public class SortWithReflection
{
    public static void main(String[] args)
    {
        List<Task> tasks = new ArrayList<Task>();
        tasks.add(new Task("AAA", 222));
        tasks.add(new Task("BBB", 333));
        tasks.add(new Task("CCC", 111));

        System.out.println("By getTitle:");
        tasks.stream()
            .sorted(by("getTitle"))
            .forEach(System.out::println);

        System.out.println("By getDate:");
        tasks.stream()
            .sorted(by("getDate"))
            .forEach(System.out::println);

        System.out.println("By getDate, reversed");
        tasks.stream()
            .sorted(by("getDate", Comparator.naturalOrder().reversed()))
            .forEach(System.out::println);

    }

    private static <T> Comparator<T> by(String methodName)
    {
        return by(methodName, Comparator.naturalOrder());
    }

    private static <T> Comparator<T> by(
        String methodName, Comparator<?> downstream)
    {
        @SuppressWarnings("unchecked")
        Comparator<Object> uncheckedDownstream =
            (Comparator<Object>) downstream;
        return (t0, t1) -> 
        {
            Object r0 = getOptional(t0, methodName);
            Object r1 = getOptional(t1, methodName);
            return uncheckedDownstream.compare(r0, r1);
        };
    }

    private static <T> T getOptional(
        Object instance, String methodName)
    {
        try
        {
            Class<?> type = instance.getClass();
            Method method = type.getDeclaredMethod(methodName);
            Object object = method.invoke(instance);
            @SuppressWarnings("unchecked")
            T result = (T)object;
            return result;
        }
        catch (NoSuchMethodException 
            | SecurityException 
            | IllegalAccessException 
            | IllegalArgumentException 
            | InvocationTargetException
            | ClassCastException e)
        {
            e.printStackTrace();
            return null;
        }
    }


    static class Task
    {
        String title;
        Integer date;

        Task(String title, Integer date)
        {
            this.title = title;
            this.date = date;
        }

        String getTitle()
        {
            return title;
        }

        Integer getDate()
        {
            return date;
        }

        @Override
        public String toString()
        {
            return title + ": " + date;
        }
    }

}

关于java - 调用Comparator比较方法中的方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58304381/

相关文章:

java - 线程退出并出现未捕获的异常 : NO stack trace

java - 使用现有的 Spring Security 通过 XML 设置 Spring Session 和 Redis

c++ - 使用从字符串中提取的参数调用函数

java - 反射。如何知道兄弟字段值?

c++ - 模板类对象的比较器,C++

java - 在Java中不使用数组按长度排序字符串

java - 在哪里以及如何链接 html 文件、css 文件、javascript 文件和 jsp

java: 未经检查的对 getConstructor(java.lang.Class<?>...) 的调用

java - 使用java中的Collections.sort()对列表中具有空值的多个字段进行排序?

java - Collections.sort 比较方法违反了它的通用契约