Ответ 1
Эта замена небезопасна во время выполнения.
Я должен сказать более точно, что это изменение само по себе безопасно; но последующие изменения, которые он поощряет, могут привести к сбоям.
Разница между Collection
и Collection<?>
Заключается в том, что вы можете добавить что-либо к первому, тогда как вы не можете добавить ничего, кроме буквального null для последнего.
Итак, кто-то, кто в настоящее время переопределяет ваш метод, может сделать что-то вроде:
@Override
public StringBuilder append(Collection value) {
value.add(Integer.valueOf(1));
return new StringBuilder();
}
(Я не знаю, для чего предназначен этот метод, это патологический пример. Это, безусловно, похоже на то, что они не должны делать, но это не то же самое, что они не делают этого).
Теперь скажем, что этот метод называется так:
ArrayList c = new ArrayList();
thing.append(c);
c.get(0).toString();
(Опять же, я не знаю, как он используется по-настоящему. Потерпите меня)
Если вы внесли параметр append
Collection<?>
Вместо этого, возможно, удивительно (*), вам не нужно было бы обновлять подкласс, чтобы он был также общим: описанный выше метод append
будет компилироваться.
Увидев новый общий тип параметра в базовом классе, вы могли бы подумать, что теперь вы можете сделать этот код вызова не сырым:
ArrayList<Double> c = new ArrayList<>();
thing.append(c);
c.get(0).toString();
Теперь, вот как получается последняя строка: в ней есть неявный листинг. На самом деле было бы оценено что-то вроде:
Double d = (Double) c.get(0);
d.toString();
Это несмотря на то, что вы можете вызывать toString()
для Object
: по-прежнему выполняется checkcast
вставленная компилятором, в стирание типа элемента списка. Это закончилось бы во время выполнения, потому что последний элемент в списке - это целое число, а не Double
.
И ключевым моментом является то, что для версии с типизированным типом не добавляется листинг. Это будет оцениваться следующим образом:
Object d = (Object) c.get(0);
d.toString();
Это не подведет во время выполнения, потому что что-то может быть передано объекту (на самом деле, вообще не было бы никакого броска, я просто вставляю его для симметрии).
Это не означает, что такой код вызова не мог существовать до создания параметра Collection<?>
: Он, конечно, мог бы, и он будет работать с ошибкой во время выполнения. Но точка, которую я пытаюсь подчеркнуть, заключается в том, что создание этого параметра метода generic может дать ошибочное впечатление, что безопасно преобразовать существующий необработанный код вызова для использования генериков, и это приведет к сбою.
Итак... Если вы не можете гарантировать, что в подкласса нет такой вставки, или вы явно задокументировали, что сбор не должен быть изменен третьим методом, это изменение не будет безопасным.
(*) Это возникает как следствие определения переопределенной эквивалентности в JLS Sec 8.4.2, где явно рассматривается стирание.