This has been asked a thousand times, but all of answers I've seen do not work on a passed Object, they are always hard coded to which Object and which field. I'm looking for a way to sort a List / Vector of based on a string field. I don't mind if it uses Reflection or Voodoo magic.
The method I've written results in a StackOverFlowError (pun intended!).
I call my method as follows:
StandardComparator.sort("distance",Vector<?>)StaticItems.LocationList,Item.SingleLocation.class);
StandardComparator class is defined as such:
public class StandardComparator {
public static void sort(final String field, Vector<?> locationList, final Class typeOfObject){
Collections.sort(locationList, new Comparator<Object>() {
@Override
public int compare(Object object1, Object object2) {
try {
return this.compare(typeOfObject.getField(field),typeOfObject.getField(field));
} catch (SecurityException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
return 0;
}
});
}
}
The error:
E/AndroidRuntime(22828): FATAL EXCEPTION: Thread-10
E/AndroidRuntime(22828): java.lang.StackOverflowError
E/AndroidRuntime(22828): at java.lang.reflect.Field.<init>(Field.java:89)
E/AndroidRuntime(22828): at java.lang.reflect.Field.<init>(Field.java:81)
E/AndroidRuntime(22828): at java.lang.reflect.ReflectionAccessImpl.clone(ReflectionAccessImpl.java:42)
E/AndroidRuntime(22828): at java.lang.Class.getField(Class.java:870)
E/AndroidRuntime(22828): at com.AtClass.Extras.StandardComparator$1.compare(StandardComparator.java:24)
A SingleLocation object:
public class SingleLocation {
int id;
public String deviceId;
public String title;
public String message;
public double latCoords;
public double lngCoords;
public String locSeen;
public Bitmap icon;
public double distance;
public String distanceString;
public SingleLocation(String id, String deviceId, String title, String message, String latCoords, String lngCoords, String locSeen){
this.id = Integer.valueOf(id);
this.deviceId = deviceId;
this.title = title;
this.message = message;
this.latCoords = Double.valueOf(latCoords);
this.lngCoords = Double.valueOf(lngCoords);
this.locSeen = locSeen;
}
}
Here's an example that will work on any Comparable field. You'd have to add special handling for primitive types and non-Comparables:
import java.lang.reflect.Field;
import java.util.*;
public class ReflectionBasedComparator {
public static void main(String[] args) {
List<Foo> foos = Arrays.asList(new Foo("a", "z"), new Foo("z", "a"), new Foo("n", "n"));
Collections.sort(foos, new ReflectiveComparator("s"));
System.out.println(foos);
Collections.sort(foos, new ReflectiveComparator("t"));
System.out.println(foos);
}
static class Foo {
private String s;
private String t;
public Foo(String s, String t) {
this.s = s;
this.t = t;
}
@Override
public String toString() {
return "Foo{" +
"s='" + s + '\'' +
", t='" + t + '\'' +
'}';
}
}
private static class ReflectiveComparator implements Comparator<Object> {
private String fieldName;
public ReflectiveComparator(String fieldName) {
this.fieldName = fieldName;
}
@Override
public int compare(Object o1, Object o2) {
try {
Field field = o1.getClass().getDeclaredField(fieldName);
if (!Comparable.class.isAssignableFrom(field.getType())) {
System.out.println(field.getType());
throw new IllegalStateException("Field not Comparable: " + field);
}
field.setAccessible(true);
Comparable o1FieldValue = (Comparable) field.get(o1);
Comparable o2FieldValue = (Comparable) field.get(o2);
return o1FieldValue.compareTo(o2FieldValue);
} catch (NoSuchFieldException e) {
throw new IllegalStateException("Field doesn't exist", e);
} catch (IllegalAccessException e) {
throw new IllegalStateException("Field inaccessible", e);
}
}
}
}