Несовместимые типы, выведенные типа, не соответствуют ограничениям (ограничениям)

Итак, у меня есть модель Model.

public class Model { .... } 

У этого есть два подкласса:

public class SubmodelA extend Model { .... }

и

public class SubmodelB extend Model { .... }

Эти три заключены в класс Data.

public class ApiData<T extends Model> {

    public T data;

}

Мой общий response wrapper выглядит следующим образом:

public class ApiResponse<DATA> {

    DATA data;
}

Операция "dummy" api остается той же:

public interface Endpoints {

    Call<ApiResponse<ApiData>> getData();
}

У меня есть реализация retrofit2.Callback для обработки ответов:

public class ApiCallbackProxy<T> implements retrofit2.Callback<T> {

    public interface ApiResultListener<RESPONSE_TYPE> {
        void onResult(RESPONSE_TYPE response, ApiError error);
    }

    private ApiResultListener<T> mListener;

    private ApiCallbackProxy(ApiResultListener<T> listener) {
        mListener = listener;
    }

    @Override
    public void onResponse(Call<T> call, Response<T> response) {

    }

    @Override
    public void onFailure(Call<T> call, Throwable t) {

    }

    public static <T> ApiCallbackProxy<T> with(ApiResultListener<T> callback) {
        return new ApiCallbackProxy<>(callback);
    }
}

ApiClient

public class ApiClient {

    public Endpoints mRetrofit;

    public ApiClient() {
       Retrofit retrofit = new Retrofit.Builder().build();
       mRetrofit = retrofit.create(Endpoints.class);
    }

    public <U extends Model> void getData(ApiResultListener<ApiResponse<ApiData<U>>> callback) {
       //Compiler hits here
       mRetrofit.getData().enqueue(ApiCallbackProxy.with(callback));
    }
}

Компилятор попадает в ApiCallbackProxy.with(callback) с этой ошибкой:

введите описание изображения здесь

Итак, я хочу, в зависимости от того, где этот API-вызов используется в приложении, чтобы вернуть другой подкласс модели или самой модели.

т.

public static void main (String[] args) {
    ApiClient apiClient = new ApiClient();
    apiClient.getData(listener2);
}


public static final ApiResultListener<ApiResponse<Data<SubmodelA>>> listener = (response, error) -> {};

public static final ApiResultListener<ApiResponse<Data<Model>>> listener2 = (response, error) -> {};

public static final ApiResultListener<ApiResponse<Data<SubmodelB>>> listener3 = (response, error) -> {};

Ответы

Ответ 1

В классе ApiClient есть слушатель, ожидающий ApiData<U> в ответе.

Проблема в том, что нет U. У вас есть Endpoint, а конечная точка не имеет общих типов и возвращает только ApiData без конкретного типа, выбранного для общего.

Это случай, когда дженерики пошли не так. Обычная идея состояла бы в том, чтобы сделать конечную точку общей:

public interface Endpoints<U> {
    Call<ApiResponse<ApiData<U>>> getData();
}

Однако, что делает Retrofit? Он превращает HTTP API в интерфейс Java. И, глядя на самый простой пример в github repo of Retrofit, мне кажется довольно понятным, что вы должны поместить интерфейсы, которые обращаются к некоторым реальной конечной точки HTTP. Это не абстрактное GET.

Итак, вам лучше дать конкретный тип, чем сделать его общим. Сделайте что-то вроде:

public interface Endpoints {
    Call<ApiResponse<ApiData<Model>>> getData();
}

Я ожидаю, что Retrofit будет десериализовать данные в ответе на ваш Model. Таким образом, для успешного десериализации важно иметь конкретный класс, а не неопределенную типичную переменную типа. Тем не менее, вы можете использовать его только с прослушивателями:

ApiResultListener<ApiResponse<Data<Model>>>
ApiResultListener<ApiResponse<Data<? super Model>>>

Кроме того, в старшей части вопроса ApiResponse<Payload>, где переменная типа generic выглядит как класс Payload, теперь это довольно коварно:)