Страницы

Поиск по вопросам

пятница, 27 декабря 2019 г.

Как корректно использование дженерики в данном примере?

#java #generics #рефлексия #аннотации #okhttp


Есть задача А, B и С. И по сути дела возможно я не корректным методом решаю задачи
A и B, но задача C следующая.
Есть интерфейс содержащий методы и аннотации: 

public interface ApiService {

    @POST("/api/v1/smart/status")
    Call postWorkChangeStatus(@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(); } } Надеюсь помог.

Комментариев нет:

Отправить комментарий