There is task A, B and C. And as a matter of fact, perhaps I am not using the correct method to solve problems A and B, but task C is the following. There is an interface containing methods and annotations:

public interface ApiService { @POST("/api/v1/smart/status") Call<SessionStatusResponse> postWorkChangeStatus(@Body WorkChangeStatusRequestData sessionStatusData); @POST("/api/v1/smart/sensor") Call<SensorDataResponse> postWorkSensorData(@Body SensorRequestData sensorData); @GET("/api/v1/smart/sensor") @FormUrlEncoded Call<AuthConfirmPhoneResponse> getWorkSensorData(@Field("number") String id); } 

I want to get a Map<String, List<String>> in which the path will be the key (the annotation value, for example "/ api / v1 / smart / sensor") and the annotation name value (if you are familiar with okhttp then you they were met). That is, they should receive in this case {"/api/v1/smart/sensor":["POST", "GET"], ...}

I get this data through reflection in the following way:

  void parse() { methods = clazz.getMethods(); for (int i = 0; i < methods.length; i++) { if (methods[i].isAnnotationPresent(POST.class)) annotationValues.put(methods[i].getAnnotation(POST.class).value(), new ArrayList<String>()); else if (methods[i].isAnnotationPresent(GET.class)) annotationValues.put(methods[i].getAnnotation(GET.class).value(), new ArrayList<String>()); else if (methods[i].isAnnotationPresent(DELETE.class)) annotationValues.put(methods[i].getAnnotation(DELETE.class).value(), new ArrayList<String>()); else if (methods[i].isAnnotationPresent(PUT.class)) annotationValues.put(methods[i].getAnnotation(PUT.class).value(), new ArrayList<String>()); else if (methods[i].isAnnotationPresent(HEAD.class)) annotationValues.put(methods[i].getAnnotation(HEAD.class).value(), new ArrayList<String>()); else if (methods[i].isAnnotationPresent(OPTIONS.class)) annotationValues.put(methods[i].getAnnotation(OPTIONS.class).value(), new ArrayList<String>()); } for (int i = 0; i < methods.length; i++) { if (methods[i].isAnnotationPresent(POST.class)) { // parse(methods[i], POST.class); String path = methods[i].getAnnotation(POST.class).value(); annotationValues.get(path).add(POST.class.getSimpleName()); } else if (methods[i].isAnnotationPresent(GET.class)) { String path = methods[i].getAnnotation(GET.class).value(); annotationValues.get(path).add(GET.class.getSimpleName()); } else if (methods[i].isAnnotationPresent(DELETE.class)) { String path = methods[i].getAnnotation(DELETE.class).value(); annotationValues.get(path).add(DELETE.class.getSimpleName()); } else if (methods[i].isAnnotationPresent(PUT.class)) { String path = methods[i].getAnnotation(PUT.class).value(); annotationValues.get(path).add(PUT.class.getSimpleName()); } else if (methods[i].isAnnotationPresent(HEAD.class)) { String path = methods[i].getAnnotation(HEAD.class).value(); annotationValues.get(path).add(HEAD.class.getSimpleName()); } else if (methods[i].isAnnotationPresent(OPTIONS.class)) { String path = methods[i].getAnnotation(OPTIONS.class).value(); annotationValues.get(path).add(OPTIONS.class.getSimpleName()); } } } 

Looks not very nice. I decided to write the following method, which accepts the method and the class of the suspected annotation:

 private <T extends Annotation> void parse(Method method, Class<T> postClass) { String test = ((T)method.getAnnotation(postClass)).value(); //ошибка! Log.d(logTag, "========TESTVALUE: " + test); } 

But a compilation error occurs, which is fully justified, since it is not known in advance whether the class T, an Annotation will inherit the value () method. The okhttp annotations did not find a common ancestor

Is it possible in any way to write a similar method or solve this problem more elegantly?

  • You can get the value by reflexion. - talex

1 answer 1

The first thing that catches your eye is methods[i].isAnnotationPresent . The most optimal of the method to get an array of declared annotations is method.getDeclaredAnnotations() . And since the array contains objects, we can get them SimpleName: annotation.annotationType().getSimpleName() and then safely wrap it in switch

The second is the duplicated code for filling the mapka. In my opinion, it is easier to put it into a separate method with all the accompanying checks.

Third, use the dependency injection pattern. parse accepts any number of objects as input and then they are analyzed. You can also make another method that will take as input the array of methods and validate / filter them. Roughly speaking, divide the code into logical parts (methods) and transfer objects to them already.

The result of executing the following Result: {/api/v1/smart/status=[GET, POST]} code Result: {/api/v1/smart/status=[GET, POST]}

 public class Main { private static Map<String, List<String>> map = new HashMap<>(); public static void main(String[] asd) throws InstantiationException, IllegalAccessException { parse(new Main(), new Integer("1")); System.out.println("Result: " + map); } private static void parse(Object... objects) { for (Object o : objects) { for (Method method : o.getClass().getDeclaredMethods()) { for (Annotation annotation : method.getDeclaredAnnotations()) { switch (annotation.annotationType().getSimpleName()) { case "POST": addToMap(((POST) annotation).value(), "POST"); break; case "GET": addToMap(((GET) annotation).value(), "GET"); break; case "DELETE": case "PUT": case "HEAD": case "OPTIONS": } } } } } private static void addToMap(String path, String httpMethod) { if (path != null) { List<String> list = map.get(path); if (list == null) { list = new ArrayList<>(); } list.add(httpMethod); map.put(path, list); } } @GET("/api/v1/smart/status") private void fooMeethod() {} @POST(value = "/api/v1/smart/status") private void barMeethod() {} @Retention(RUNTIME) @Target({METHOD}) private @interface GET { String value(); } @Retention(RUNTIME) @Target({METHOD}) private @interface POST { String value(); } } 

I hope helped.

  • Yes, thanks for the answer. Your decision is a concise continuation and development of my thought. But unfortunately it does not solve my original problem - the problem of the hardcode name of the interface annotations (I mean, the switch on the line). - abbath0767