2012-11-26 15 views
5

Ho un arraylist di VO. Questi oggetti hanno molte proprietà e metodi get/set corrispondenti. Voglio ordinare questo elenco di array in base a una proprietà che otterrò in fase di esecuzione. Lasciatemi spiegare in dettaglio. Il mio VO è come questoordina una lista di oggetti basata sulla proprietà di runtime

public class Employee { 
    String name; 
    String id; 

    private String getName() { 
     return name; 
    } 

    private String getId() { 
     return id; 
    } 
} 

sarò sempre uno stringa ‘sortType’ in fase di esecuzione, che può essere sia ‘id’ o ‘nome’. Voglio ordinare l'elenco in base al valore della stringa.

Ho provato a utilizzare il comparatore e il riflesso insieme, ma senza fortuna. Forse non l'ho usato correttamente. Non voglio usare un ciclo if e creare nuove classi di confronto. Qualche altro pensiero?

Il fermo di prova dovrebbe trovarsi all'interno della nuova classe. Ecco il codice funzionante. Se si desidera utilizzare una classe separata per il comparatore, si prega di trovarlo nel commento di @ Bohemian qui sotto.

 String sortType = "name"; // determined at runtime 
     Collections.sort(results, new Comparator<Employee>() { 
     public int compare(Employee c1, Employee c2) { 
      try{ 
      Method m = c1.getClass().getMethod("get" + StringUtils.capitalize(sortType)); 
      String s1 = (String)m.invoke(c1); 
      String s2 = (String)m.invoke(c2); 
      return s1.compareTo(s2); 
      } 
      catch (Exception e) { 
       return 0; 
      } 
     } 
     }); 
+2

prega usa il tag della lingua appropriato. – NPE

+0

Oops .. L'ho aggiunto ora. – jijo

+0

Se pubblichi il tuo codice che utilizzava il riflesso, potremmo essere in grado di dirti cosa è andato storto ... – akuhn

risposta

15

Creare un Comparator per il lavoro:

public class EmployeeComparator implements Comparator<Employee> { 

    private final String type; 

    public EmployeeComparator (String type) { 
     this.type = type; 
    } 

    public int compare(Employee e1, Employee e2) { 
     if (type.equals("name")) { 
      return e1.getName().compareTo(e2.getName()); 
     } 
     return e1.getId().compareTo(e2.getId()); 
    } 

} 

Poi per usarlo

String type = "name"; // determined at runtime 
Collections.sort(list, new EmployeeComparator(type)); 

La versione riflessiva sarebbe simile, ad eccezione cercheresti un metodo sull'oggetto di "get" + tipo (in maiuscolo) e invocalo e lo lanci a Paragonabile e usa compareTo (cercherò di mostrare il codice, ma sto usando il mio iPhone ed è un un po 'troppo, b ut va qui)

public class DynamicComparator implements Comparator<Object> { 
    private final String type; 
    // pass in type capitalised, eg "Name" 
    // ie the getter method name minus the "get" 
    public DynamicComparator (String type) { 
     this.type = type; 
    } 
    public int compare(Object o1, Object o2) { 
     // try-catch omitted 
     Method m = o1.getClass().getMethod("get" + type); 
     String s1 = (String)m.invoke(o1); 
     String s2 = (String)m.invoke(o2); 
     return s1.compareTo(s2); 
    } 
} 

OK ... Ecco come farlo senza creazione di una classe, utilizzando una classe anonima (con la gestione delle eccezioni in modo codice viene compilato):

List<?> list; 
final String attribute = "Name"; // for example. Also, this is case-sensitive 
Collections.sort(list, new Comparator<Object>() { 
    public int compare(Object o1, Object o2) { 
     try { 
      Method m = o1.getClass().getMethod("get" + attribute); 
      // Assume String type. If different, you must handle each type 
      String s1 = (String) m.invoke(o1); 
      String s2 = (String) m.invoke(o2); 
      return s1.compareTo(s2); 
     // simply re-throw checked exceptions wrapped in an unchecked exception 
     } catch (SecurityException e) { 
      throw new RuntimeException(e); 
     } catch (NoSuchMethodException e) { 
      throw new RuntimeException(e); 
     } catch (IllegalAccessException e) { 
      throw new RuntimeException(e); 
     } catch (InvocationTargetException e) { 
      throw new RuntimeException(e); 
     } 
    } 
}); 
+1

Questo sembra buono e semplice. Grazie !! Proverò a farti sapere. – jijo

+0

Questo è sicuramente più pulito, +1 per quello ... –

+0

Uomo fantastico .. !! Questo è esattamente quello che stavo cercando!Fammi provare. – jijo

0

Keep it simple!

Se la scelta è solo id o name -use un'istruzione if.

Scegliere tra due opzioni. Questo è quello che se è stato inventato per.

Oppure, se sono molte proprietà, utilizzare la riflessione o memorizzare i dati in un Map in primo luogo. A volte Map è meglio di una classe. In particolare se il tuo VO non ha metodi diversi da getter e setter.

Attenzione però, l'uso della riflessione potrebbe non essere sicuro in questo caso poiché il client potrebbe iniettare qualsiasi termine nei parametri CGI in un attacco simile all'iniezione SQL.

+0

L'OP afferma che la sua classe ha proprietà _many_. Penso che abbia appena mostrato due di loro per renderlo semplice. – jahroy

+0

Questo è il problema. Non è solo id e nome, il mio VO è abbastanza grande con circa 10+ proprietà in esso. – jijo

+0

Aggiunto razionale sul perché * NON * usando la riflessione. – akuhn

1

Effettuare le seguenti operazioni:

  • ottenere il nome del campo da parte del cliente
  • costruire il nome del getter - + nome> "get" campo (dopo capitalizzando il primo carattere)
  • cercare di trovare il metodo con la riflessione utilizzando Class.getDeclaredMethod()
  • se trovato, richiamare l'oggetto metodo tornato su due istanze del VO classe
  • utilizzare i risultati dei metodi getter invocati per l'ordinamento
+0

Si potrebbe anche accedere ai campi (se i getter sono davvero così banali). – akuhn

Problemi correlati