Обобщения имеют тенденцию решать проблему того, что я считаю «наивными приведениями» в Java 1.4 или более ранних версиях при работе с коллекциями. В Java 1.5+ строка, которую вы поместили:
ArrayList<Row> rows = new ArrayList();
выдаст предупреждение, правильный общий код:
ArrayList<Row> rows = new ArrayList<Row>();
Это говорит компилятору, что ваш объект ArrayList должен содержать только тип Row.
Однако, поскольку Java 1.5 обратно совместима с огромными наборами библиотек, которые не содержат этот синтаксис, а скорее ваш предыдущий код:
ArrayList rows = new ArrayList();
Дженерики, очевидно, не будут работать с этими старыми библиотеками - поэтому дженерики являются только опцией времени компиляции - классы 1.5 и 1.4 фактически эквивалентны (за исключением любых внутренних методов рефакторинга / новых методов, добавленных позже), потому что они на самом деле реализации ArrayList, которые обрабатывают любой тип объекта.
Код 1.5 просто добавляет прямое приведение для вас.
В коде 1.4, скажем, вы хотите перебрать ArrayList. Наивный способ сделать это заключается в следующем:
for(Iterator rowIterator = rows.iterator(); rowIterator.hasNext(); ) {
Row row = (Row) rowIterator.next();
// Do something with the row.
}
Код Java 1.5 в точности эквивалентен наивной версии. Принимается тот факт, что вы сообщаете компилятору, что он является строкой и выполняет этот код за вас. Итак, преимущества синтаксического сахара более приятны (при этом используется новый синтаксис для каждого цикла, но генерируется тот же код, что и в предыдущем цикле):
for(Row row : rows) {
// Do something with the row
}
Итак, если вы хотите использовать ArrayList, содержащий только строки, вы все равно можете это сделать. Но нет никакого способа заставить компилятор проверить , что ArrayList содержит только все строки (хотя, хотя компилятор обеспечивает эту проверку, все еще можно отправить в ArrayList, который содержит другие типы объектов, поскольку, опять же, ArrayList все еще действительно обрабатывает тип Object, а универсальные значения стираются во время выполнения - все, что остается, - это наивный код приведения, который вы больше не видите).
Не наивный вариант состоит в том, чтобы проверять каждый экземпляр и генерировать исключение ClassCastException самостоятельно с информативным сообщением (вместо того, чтобы программа выдавала его с сообщением по умолчанию):
for(Iterator rowIterator = rows.iterator(); rowIterator.hasNext(); ) {
Object shouldBeRow = rowIterator.next();
if(!(shouldBeRow instanceof Row)) {
throw new ClassCastException("The object " + shouldBeRow + " is not an instance of Row - only Rows should be present in the list!");
}
Row row = (Row) shouldBeRow;
// Do something with the row.
}
Однако, как правило, никто не делает этого - хорошая документация может спровоцировать эту проблему, так как это накладывает бремя обеспечения правильной коллекции на вызывающую сторону, и, таким образом, вы можете просто вызвать ClassCastException, выдаваемый JVM.