Передать параметризованный тип методу в качестве аргумента

Этот класс из библиотеки продавца:

public class JsonParser {
    public <T> T parse(String json, Class<T> type) { ... }
}

Это мои модели:

public class Video {
    @Key
    private String title;
    public String getTitle() {
        return title;
    }
}

public class Response<TResult> {
    @Key
    private TResult result;
    public TResult getResult() {
        return result;
    }
    // ...
}

Этот код работает:

JsonParser parser = new JsonParser();
String json = "{ \"title\": \"Hello world\" }";
Video video = parser.parse(json, Video.class);

Этот код не работает: (синтаксическая ошибка в Response<Video>.class)

JsonParser parser = new JsonParser();
String json = "{ \"result\" : { \"title\": \"Hello world\" } }";
Response<Video> videoResponse = parser.parse(reader, Response<Video>.class);

Этот код работает:

public class VideoResponse extends Response<Video> {
}

...
JsonParser parser = new JsonParser();
String json = "{ \"result\" : { \"title\": \"Hello world\" } }";
Response<Video> videoResponse = parser.parse(reader, VideoResponse.class);

Мой вопрос: как пройти Response<Video> класс для parse метод в качестве параметра без создания VideoResponse как это. (В моей программе есть много моделей, похожих на VideoЯ не хочу дублировать мой код для создания пустых классов VideoResponse, UserResponse, CommentResponse, ActivityResponse, так далее)

2 ответа

Решение

Из-за того, как реализованы общие шаблоны Java, в большинстве случаев общая информация теряется во время выполнения. Одним из исключений для этих так называемых reifiable типов являются конкретные расширения универсальных классов. Для вашего первого примера:

public class Video {
    @Key
    private String title;
    public String getTitle() {
        return title;
    }
}

public class Response<TResult> {
    @Key
    private TResult result;
    public TResult getResult() {
        return result;
    }
    // ...
}

Парсер не сможет десериализовать result свойство, поскольку он не сможет определить, какой это тип (поскольку эта информация недоступна во время выполнения). В принципе, разбор просто видит java.lang.Objectи не может определить тип для создания экземпляра данных JSON. Я предполагаю, что вы уже подозреваете, что это так, поэтому попытка сделать этот вызов:

Response<Video> videoResponse = parser.parse(reader, Response<Video>.class);

В приведенной выше строке вы пытаетесь сообщить парсеру, что конкретный ответ параметризован с помощью Video, но, к сожалению, Java не имеет синтаксиса для литералов универсального класса, поэтому код не компилируется.

В вашем втором примере:

public class VideoResponse extends Response<Video> {
}

Response<Video> videoResponse = parser.parse(reader, VideoResponse.class);

Вы создали конкретное расширение вашего общего класса. Для таких расширений информация общего типа доступна во время выполнения, так что ваш анализатор может определить, что ему нужно создать для десериализации ваших данных JSON.

Все это справочная информация к вашему актуальному вопросу:

Мой вопрос: как передать класс Response методу разбора в качестве параметра, не создавая VideoResponse, как это

Вы не упомянули, какую библиотеку JSON вы используете, но в большинстве популярных библиотек методы десериализации имеют переопределенную версию, которая принимает так называемый токен супертипа. Маркер супертипа - это просто конкретное расширение класса, подобное тому, что я описал выше. Например, в Джексоне вы бы десериализовали свой JSON следующим образом:

Response<Video> response = new ObjectMapper().readValue(
    jsonString, // JSON data
    new TypeReference<Response<Video>>() {} // super type token, implemented by anonymous class
);

Вы должны проверить документацию JSON-библиотек на предмет чего-либо подобного.

Из-за "стирания типов" универсальных типов Java вы должны вместо этого явно использовать приведение типов следующим образом:

Response<Video> videoResponse = (Response<Video>) parser.parse(reader, Response.class);

Будет введено предупреждение о компиляции, но ничего страшного.

Другие вопросы по тегам