#java #generics #рефлексия #аннотации #okhttp
Есть задача А, B и С. И по сути дела возможно я не корректным методом решаю задачи A и B, но задача C следующая. Есть интерфейс содержащий методы и аннотации: public interface ApiService { @POST("/api/v1/smart/status") CallpostWorkChangeStatus(@Body WorkChangeStatusRequestData sessionStatusData); @POST("/api/v1/smart/sensor") Call postWorkSensorData(@Body SensorRequestData sensorData); @GET("/api/v1/smart/sensor") @FormUrlEncoded Call getWorkSensorData(@Field("number") String id); } Я хочу получить Map > в которой в качестве ключа будет выступать путь (значение аннотации, например "/api/v1/smart/sensor") и в качестве значения имя аннотации (если вы знакомы с okhttp то вы их встречали). То есть должны получить в данном случае {"/api/v1/smart/sensor":["POST", "GET"], ...} Получаю эти данные с помощью рефлексии следующим способом: 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 ()); else if (methods[i].isAnnotationPresent(GET.class)) annotationValues.put(methods[i].getAnnotation(GET.class).value(), new ArrayList ()); else if (methods[i].isAnnotationPresent(DELETE.class)) annotationValues.put(methods[i].getAnnotation(DELETE.class).value(), new ArrayList ()); else if (methods[i].isAnnotationPresent(PUT.class)) annotationValues.put(methods[i].getAnnotation(PUT.class).value(), new ArrayList ()); else if (methods[i].isAnnotationPresent(HEAD.class)) annotationValues.put(methods[i].getAnnotation(HEAD.class).value(), new ArrayList ()); else if (methods[i].isAnnotationPresent(OPTIONS.class)) annotationValues.put(methods[i].getAnnotation(OPTIONS.class).value(), new ArrayList ()); } 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()); } } } Выглядит не очень красиво. Решил написать следующий метод, который принимает метод и класс подозреваемой аннотации: private void parse(Method method, Class postClass) { String test = ((T)method.getAnnotation(postClass)).value(); //ошибка! Log.d(logTag, "========TESTVALUE: " + test); } Но возникает ошибка компиляции, которая вполне оправдана, так как заранее не известно, будет ли у класса Т, наследника Annotation метод value(). У аннотаций okhttp общего предка не нашел. Возможно ли каким либо образом написать подобный метод или решить данную задачу элегантнее?
Ответы
Ответ 1
Первое что бросается в глаза это methods[i].isAnnotationPresent. Самое оптимальное из метода получать array задекларированных аннотаций method.getDeclaredAnnotations(). А так как array содержит объекты, то мы можем получить их SimpleName: annotation.annotationType().getSimpleName() и далее благополучно заворачиваем в switch Второе - дублированный код наполнения мапки. На мой взгляд это проще вынести в отдельный метод со всеми сопутствующими проверками. Третье - использовать паттерн dependency injection. parse принимает на вход любое количество объектов и далее они анализируются. Так же можно сделать еще один метод который будет принимать на вход array методов и валидировать/фильтровать их. Грубо говоря разделить код на логические части (методы) и передавать уже в них объекты. Результат выполнения ниже приведенного кода Result: {/api/v1/smart/status=[GET, POST]} public class Main { private static Map> 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 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(); } } Надеюсь помог.
Комментариев нет:
Отправить комментарий