I have a custom annotation that I want to use at runtime to display object properties. I would like them to appear in source code order but reflection does not guarantee any particular order for Class.getMethods()
.
Is there a way, either via reflection or via annotation processing, to get the methods in source order (at least per class if multiple levels of inheritance are involved)?
As an example, say I had an interface Property
package test;
public @interface Property {
public String name();
}
and a class using that annotation
package test;
public class MyObject {
@Property(name = "First")
public void getFirst() {}
@Property(name = "Another")
public void getAnother() {}
}
I'd like to reliably get the property "First" before the property "Another".
I know I can add an ordering property to my annotation and sort on that but I have a lot of classes that would need to be updated if that is required so I'm looking for a generic method to achieve this without modifying individual annotations.
It appears this is not possible via reflection unless you are willing and able to change the original source to impose an order via an additional annotation or attribute of an existing annotation.
It is possible, however, during annotation processing. The documentation for TypeElement.getEnclosedElements()
] indicates that
the list of enclosed elements will be returned in the natural order for the originating source of information about the type. For example, if the information about the type is originating from a source file, the elements will be returned in source code order [emphasis added].
To make this available at runtime, you need to process the methods with the annotations and store somewhere that will be accessible at runtime (e.g a generated resource file).
If you collect the annotations into aList<Property>
you can order the List
like any other collection using Collections.sort(collection, comparator)
. The main issue is there is no natural order to how the annotations should be ordered, so you will need to define this order. I have defined the order via a List
which is used in the comparator.
public class MyObject {
@Property(name = "First")
public void getFirst() {
}
@Property(name = "Another")
public void getAnother() {
}
@Property(name = "Last")
public void getLast() {
}
public static void main(String[] args) {
Method[] methods = MyObject.class.getDeclaredMethods();
List<Property> properties = new ArrayList<Property>();
for(Method method: methods){
if(method.isAnnotationPresent(Property.class)){
properties.add(method.getAnnotation(Property.class));
}
}
for(Property property:properties){
System.out.println(property.name());
}
Collections.sort(properties, new Comparator<Property>(){
List<String> order = Arrays.asList("Last", "First", "Another");
@Override
public int compare(Property arg0, Property arg1) {
//Compare only considers the sign of result.
return (order.indexOf(arg0.name()) - order.indexOf(arg1.name()));
}
});
for(Property property:properties){
System.out.println(property.name());
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With