Я собираюсь выкинуть действительно противоречивое мнение. Обобщения усложняют язык и усложняют код. Например, скажем, у меня есть карта, которая отображает строку в список строк. В старые времена я мог объявить это просто как
Map someMap;
Теперь я должен объявить это как
Map<String, List<String>> someMap;
И каждый раз, когда я передаю его в какой-то метод, мне приходится повторять это большое длинное объявление снова и снова. На мой взгляд, вся эта лишняя типизация отвлекает разработчика и выводит его из «зоны». Кроме того, когда код заполнен большим количеством беспорядка, иногда бывает трудно вернуться к нему позже и быстро просеять все это, чтобы найти важную логику.
Java уже имеет плохую репутацию одного из самых многословных языков общего пользования, и дженерики только усугубляют эту проблему.
А что вы действительно покупаете за все это лишнее многословие? Сколько раз у вас действительно возникали проблемы, когда кто-то помещал целое число в коллекцию, которая должна содержать строки, или когда кто-то пытался извлечь строку из коллекции целых чисел? За мой 10-летний опыт работы над созданием коммерческих Java-приложений это никогда не было большим источником ошибок. Так что я не совсем уверен, что вы получаете за лишнее многословие. Это действительно кажется мне сверхбюрократическим багажом.
Теперь я собираюсь стать действительно спорным. То, что я вижу как самую большую проблему с коллекциями в Java 1.4, - необходимость повсеместной типизации. Я рассматриваю эти типы типов как лишний, многословный материал, в котором есть много тех же проблем, что и у дженериков. Так, например, я не могу просто сделать
List someList = someMap.get("some key");
Я должен сделать
List someList = (List) someMap.get("some key");
Причина, конечно, в том, что get () возвращает Object, который является супертипом List. Таким образом, назначение не может быть выполнено без приведения типов. Опять же, подумайте о том, сколько это правило действительно покупает у вас. По моему опыту, не очень.
Я думаю, что Java была бы намного лучше, если бы 1) она не добавляла дженерики, а 2) вместо этого позволяла неявное приведение из супертипа к подтипу. Пусть неправильные броски будут пойманы во время выполнения. Тогда я мог бы иметь простоту определения
Map someMap;
и позже, делая
List someList = someMap.get("some key");
все бесполезные вещи исчезнут, и я действительно не думаю, что буду вносить большой новый источник ошибок в мой код.