#java #generics
#java #дженерики
Вопрос:
Я работаю со старым Java-приложением, которое было написано для Java 1.4, до того, как дженерики были доступны. С тех пор мы завершили наше обновление до Java 6.
Сейчас мы рассматриваем возможность внесения изменений для поддержки дженериков в наших коллекциях в интересах проверки безопасности типов во время компиляции и для сохранения работоспособности разработчика.
Есть ли какие-либо оговорки или подводные камни, о которых мы должны знать при выполнении этого обновления?
Комментарии:
1. Смотрите Дженерики на языке программирования Java . Особенно параграф 10 «Преобразование устаревшего кода для использования дженериков».
2. Совет, содержащийся в этом документе, безусловно, стоит прочитать: есть несколько реальных ошибок, особенно если вы разрабатываете API совместно с другими пользователями, а не автономное приложение.
3. Спасибо. Мне достаточно повезло, что мой код предназначен для отдельного приложения, а не для API. Другим может не так повезти.
Ответ №1:
- Поймите, как работают дженерики Java, и их ограничения.
- Используйте IDE с хорошей поддержкой рефакторинга.
- Делайте это медленно и осторожно.
- Сначала выполните простые действия и не переходите к созданию сложных универсальных классов и т.д., Пока не убедитесь, что они действительно необходимы.
- Не поддавайтесь искушению добавить
@SuppressWarning("unchecked")
, чтобы убрать предупреждения.
Здесь нет ничего особенно глубокого…
Комментарии:
1. 1: Аннотация, которую вы хотите избежать использования,
@SuppressWarning("unchecked")
Примечание: Даже коллекции JDK компилируются с этими предупреждениями. 🙁2. @Peter — да, в некоторых случаях аннотации неизбежны. Но смысл в том, что его следует использовать в качестве последнего средства.
3. «непроверенный» должно быть строкой в нижнем регистре. Я думаю, что большинство людей избегают использования, не зная, что оно есть. 😉
4. Можете ли вы привести пример, когда непроверенного предупреждения нельзя было избежать с помощью <?> и приведения? Не думаю, что я когда-либо видел такое
5. @Stephen C, я человек, у которого разные предупреждения компилятору, которые я, как правило, непоследовательно игнорирую. 😉
Ответ №2:
Наиболее важная часть обновления, ИМХО, заключается в том, чтобы убедиться, что разработчики, работающие над ним, хорошо понимают дженерики, особенно в том, что касается передачи коллекций в функции и возврата их из функций, as List<Animal> <> List<Tiger>
и т.д. Вы всегда должны иметь возможность вставлять дженерики, даже если вы используете универсальный объект или? заполнители.
Другая вещь, которая сделает преобразование плавным или нет, зависит от кода, что у вас есть коллекции, содержащие один тип, что нет смешанных объектов, которые заставляют ? или объект ‘универсальный’. Это может быть довольно распространенным явлением, особенно с картами.
Если вы используете какие-либо базовые библиотеки, Hibernate и т.д., Вы можете столкнуться с ограничениями, если базовая библиотека не поддерживает дженерики. В этом случае вам придется либо пропустить библиотечный код через, либо обернуть объекты (не рекомендуется).
Измените части, которые имеют смысл, оставьте части, которые этого не делают, и делайте это поэтапно, чтобы вы всегда могли откатить часть, если столкнетесь с проблемой.
Ответ №3:
Стивен в курсе, но я бы добавил несколько советов.
1) Поскольку дженерики используются только во время компиляции, они бесполезны в нескольких местах, таких как отражение и хранение объектов в сеансе (если это веб-проект). Я не знаю никакого чистого способа справиться с этим. Я рекомендую просто объявлять все, что получается в результате, как <?>
и просто заниматься проверкой типа.
2) Это тот, который обжигал меня в прошлом. В определениях методов используйте List<? extends Shape>
, а не List<Shape>
. Если у вас есть Circle
тип, который является дочерним для Shape
, то List<Circle>
он не может быть передан в List<Shape>
, но он может быть передан в List<? extends Shape>
. Это кажется незначительным, но для меня это произошло очень внезапно и потребовало множества дополнительных изменений в классах, которые, как я думал, были сделаны.
3) Я не думаю, что вам КОГДА-либо понадобится подавлять предупреждения о дженериках, поскольку вы всегда можете превратить List
in в List<?>
a и избавиться от них. Однако я рекомендую вам не делать этого, если только эта часть кода не может использовать дженерики (см. Мой первый пункт). Лучше иметь предупреждения, которые вы планируете исправить, чем плохие исправления, которые вы планируете улучшить, поскольку вы, скорее всего, никогда их не улучшите, и предупреждения будут бросаться вам в глаза каждый раз, когда вы смотрите на свой проект.