2016-09-13 3 views
2

У меня есть класс Report {String name, Date date, int score }. Я хочу, чтобы иметь возможность сортировать список отчетов для любой переменной члена с использованием нового синтаксиса Java 8Как передать переменное имя поля/метода в Comparator.comparing

Так ява 8 обеспечивает этот новый

list.sort(Comparator.comparing(report -> report.name)) 

для сортировки списка по имени.

Скажем вместо имени, я хочу указать имя переменной для этого метода, например. что-то вроде

list.sort(Comparator.comparing(report -> report.anyField)) 

, где anyField может быть назван или дата или оценка Как достичь такого поведения.

+0

Просто введите название поля ??? – Spotted

+2

Чтобы быть ясным, проблема заключается в том, что у вас есть имя поля для сортировки (например, в String), и вы хотите сортировать по нему? – Tunaki

ответ

2

Один очень (например, INT, который не реализует Сопоставимые в отличие от Integer.) общее решение заключается в использовании в Java Reflection и некоторую отливка:

String sortBy = "name"; 
    list.sort(Comparator.comparing(report -> { 
     try { 
      return (Comparable) report.getClass().getDeclaredField(sortBy).get(report); 
     } catch (Exception e) { 
      throw new RuntimeException("Ooops", e); 
     } 
    }));  

Если вы используете дополнительную библиотеку как https://github.com/jOOQ/jOOR код становится еще проще:

String sortBy = "score"; 
    list.sort(Comparator.comparing(report -> Reflect.on(report).field(sortBy).get())); 

Обратите внимание, что это решение работает только с полями, которые реализуют Comparable и что у него есть некоторые служебные данные во время выполнения.

+2

Raw type использование. В этом проблема - во время выполнения вы не знаете, что вы сравниваете. – RealSkeptic

+4

Я бы рекомендовал Reflection здесь, если поля отчета неизвестны во время компиляции. Если набор полей фиксирован (имя, дата, оценка), то я думаю, что есть более чистые решения. – Puce

0

Если отчет имеет метод получения различного поля вы можете сделать, как этот

list.sort(Comparator.comparing(Report::getFieldName)); 

Или с лямбда-выражения

list.sort((ob1, ob2) -> ob1.getFieldName().compareTo(ob2.getFieldName())); 
+1

Это действительно ответит на вопрос? Я не вижу, как это помогает OP передавать различное имя поля. –

+0

Дэвид почему бы и нет, OP может вызывать другой метод, например getFieldName, getAnyField –

+5

OP хочет иметь возможность использовать имя поля, не принимая во внимание заранее, во время компиляции, какое поле будет использоваться. В противном случае OP может просто ввести имя поля. В чем разница между вводом имени геттера или вводом имени поля? – RealSkeptic

0

На каком-то месте, вам придется использовать немного отражения чтобы снять это. Вот пример использования механизма боб интроспекции Java:

public static class MyBean { 
     private String name; 
     private Date birthday; 
     private Integer score; 
     ... 
     ... (constructor, getters, setters - the usual bean stuff) 
    } 

    PropertyDescriptor[] pdesc = Introspector.getBeanInfo(MyBean.class).getPropertyDescriptors(); 
    for(PropertyDescriptor pd : pdesc) { 
     if(Comparable.class.isAssignableFrom(pd.getPropertyType())) { 
      System.out.println("Property " + pd.getName() + " could be used for comparison"); 

      Function<MyBean, Comparable> extractor = b -> { 
       try { 
        return (Comparable) pd.getReadMethod().invoke(b); 
       } 
       catch(Exception e) { 
        throw new RuntimeException(e); 
       } 
      }; 

      Comparator<MyBean> comp = Comparator.comparing(extractor); 

      // do something useful with the comparator :-) 
     } 
    } 

Кроме того, вы могли бы пойти на подобный путь для примитивных типов

0

Если набор свойств фиксировано (имя, дата, оценка), то я думаю, что чистый путь будет проходить компаратор:

private void someMethod(Comparator<Report> comparator){ 
    ... 
    list.sort(comparator); 
    ... 
} 

... 

someMethod(Comparator.comparing(report::getName)); 
someMethod(Comparator.comparing(report::getDate)); 
someMethod(Comparator.comparingInt(report::getScore)); 
someMethod(Comparator.comparing(report::getName).thenComparingInt(report::getScore)); 
+2

Я думаю, что вам также не хватает точки OP, которая заключается в том, что выбор поля должен быть динамическим. То есть во время выполнения он имеет имя поля, а не во время компиляции. – RealSkeptic

+0

@RealSkeptic, поскольку я понимаю вопрос, что набор свойств исправлен. Просто свойство, которое использовалось для сортировки, не исправить. – Puce

+1

Справа. Поэтому во время выполнения вы знаете только, хотите ли вы использовать 'report :: getName',' report :: getDate' и т. Д. - поэтому ваш ответ не затрагивает вопрос о выборе динамически. Полагаю, вы могли бы сказать «Использовать оператор switch», но вы этого не сделали, поэтому вы не отвечаете на вопрос OP. – RealSkeptic

3

Просто создайте компаратор для каждого свойства.

static Map<String,Comparator<Report>> ORDER; 
static { 
    HashMap<String,Comparator<Report>> m=new HashMap<>(); 
    m.put("name", Comparator.comparing(r -> r.name)); 
    m.put("date", Comparator.comparing(r -> r.date)); 
    m.put("score", Comparator.comparingInt(r -> r.score)); 
    ORDER=Collections.unmodifiableMap(m); 
} 
public static void sort(List<Report> list, String order) { 
    Comparator<Report> c=ORDER.get(order); 
    if(c==null) throw new IllegalArgumentException(order); 
    list.sort(c); 
} 

Вы можете рассмотреть возможность использования enum в качестве альтернативы String, что исключает возможность предоставления несуществующее имя свойства:

enum ReportOrder { 
    NAME(Comparator.comparing(r -> r.name)), 
    DATE(Comparator.comparing(r -> r.date)), 
    SCORE(Comparator.comparingInt(r -> r.score)); 

    private Comparator<Report> cmp; 
    private ReportOrder(Comparator<Report> c) { cmp=c; } 

    public void sort(List<Report> list) { 
     list.sort(cmp); 
    } 
} 

Теперь вы можете просто сказать, например ReportOrder.NAME.sort(list);.Конечно, другой стиль делегации работает хорошо:

public static void sort(List<Report> list, ReportOrder o) { 
    list.sort(o.cmp); 
} 

sort(list, ReportOrder.DATE); 
0

Существует класс Comparator называется NamedMethodComparator, который будет работать в качестве компаратора для любого метода нулевого агда, возвращающего Сопоставимые размещена здесь: How to use Comparator in Java to sort

Смежные вопросы