Generics, Примитивы и мечты о Valhalla

Ах, Generics и примитивы... Эта тема, которая заставляет многих новичков в Java удивлённо чесать затылок. Ну как так-то? Вот вроде бы всё логично, обобщённые типы позволяют писать красивый код, но как только ты пытаешься передать в них int или double, компилятор вежливо говорит "Ну уж нет, давай-ка сюда объект". Что за дичь?

Немного из истории (без занудства, обещаю). Java придумали в 90-х, когда люди ещё ходили в джинсовых куртках и слушали Nirvana. Generics в языке тогда и близко не было. Всё это богатство добавили только в Java 5. Но вот беда, старый добрый JVM, на котором крутится Java-код, уже был построен по своим устоявшимся правилам, и менять его под обобщения никто особо не хотел. Зачем ломать то, что работает? Поэтому придумали хитрую штуку — type erasure (стиратель типов).

Type erasure  это как волшебство, но вместо того, чтобы делать код проще, оно скрывает реальный тип Generics. После компиляции весь твой красивый List<string> превращается просто в List<Object>. JVM не знает ни о каких обобщённых типах, потому что их буквально стёрли. Это и позволяет старым версиям Java спокойно запускать код с Generics. Но тут вылезает подвох, примитивы, такие как int или double, не являются объектами в Java. А значит, их нельзя просто так запихнуть в Object.

Хочешь использовать int в Generics? Пожалуйста, но через класс-обёртку Integer. Java сделает за тебя всю грязную работу, упакует примитив в объект (это называется "автобоксинг") и распакует обратно, когда нужно. Правда, за всё это придётся платить дополнительными накладными расходами, памятью, временем выполнения и, конечно же, твоими нервами.

В примере на изображении мы создаём List<Object>, потому что List<int> не поддерживается. Java автоматически превращает int в Integer при добавлении в список. При извлечении значения из списка Integer обратно превращается в int.

Ты, наверное, думаешь "Ну ладно, в 90-х это было оправдано, но сейчас-то что мешает добавить поддержку примитивов в Generics?". А вот тут дело в том, что ломать совместимость с прошлым — это, мягко говоря, не в стиле Java. У языка строгие принципы, лучше оставить всё как есть, чем устраивать революцию и рисковать поломать кучу старого кода.

На самом деле, Java двигается вперёд. В последние годы активно обсуждаются проекты вроде Valhalla, которые обещают внести поддержку "value types" (значимых типов). Это такие себе гибриды между объектами и примитивами, они должны быть лёгкими, как примитивы, но работать как объекты. Ну, будем надеяться, что в ближайшие пару версий Java это произойдет. Но пока придётся довольствоваться обёртками и автобоксингом.

Так что, если в следующий раз тебе придётся объяснять коллегам, почему List<int> невозможен, ты можешь с умным видом рассказать им эту историю. И, может быть, тогда они перестанут злиться на Java и начнут ценить её за стабильность. Или хотя бы за то, что автобоксинг работает. 😊

https://t.me/JavaDevChat

Generics, Примитивы и мечты о Valhalla | Сетка — социальная сеть от hh.ru