Я бы сказал, что это ограничение компилятора в отношении параметров универсального типа. Компилятор знает, что вы параметризовали класс, но у него нет контекста того, как он используется.
Ваш Future
является держателем данных. Вы не можете ничего в него вкладывать, вы можете только получать запросы от него. В этом случае ваше ожидание того, что Future<? extends Response>
и Future<Response>
будут вести себя одинаково и, таким образом, будет безопасно разыгрываться, вполне разумно. Обе эти вещи являются поставщиками запросов. Точные экземпляры могут быть подклассами, но все, что мы получим из нашего Future
, определенно реализует методы Request
, так что это приведение кажется безопасным.
Проблема возникает, когда у вас есть классы, которые не являются чистыми поставщиками, но когда они потребляют данные. Предположим, мы добавили метод к Future
, чтобы установить значение:
interface Future<T>
{
void setValue(T value);
//...
}
И у нас есть Future<? extends Response>
, который был создан как Future<ChildResponse>
. Когда мы разыгрываем его Future<Response>
, мы можем теперь вызвать setValue
с Response
, тогда как перед сотворением нам понадобится ChildResponse
. Это причина, по которой актерский состав не считается безопасным.
Фактически, компилятор не достаточно умен, чтобы различать два случая, и поэтому лучшее решение - всегда объявлять приведение как небезопасное. Даже если компилятор может провести различие, проблема еще более усложняется тем фактом, что интерфейс может измениться - предположим, что Future
были изменены, как описано выше, - так что приведение, которое ранее было безопасным, больше не будет.
В этом случае я бы лично чувствовал себя прекрасно, подавляя предупреждение. Кастинг - это создание утверждений компилятору о том, что у вас есть информация, которой у него нет, какова у вас ситуация.