Необходим некоторый контекст, чтобы полностью понять основную причину этого.
Примитивы против классов
Примитивные переменные в Java содержат значения (целое число, двоичное число с плавающей запятой двойной точности и т. Д.). Поскольку эти значения могут иметь разную длину , переменные, содержащие их, также могут иметь разную длину (рассмотрим по float
сравнению с double
).
С другой стороны, переменные класса содержат ссылки на экземпляры. Ссылки обычно реализуются как указатели (или что-то очень похожее на указатели) на многих языках. Эти вещи , как правило , имеют одинаковый размер, независимо от размеров случаев они относятся ( Object
, String
, Integer
и т.д.).
Это свойство переменных класса делает ссылки, которые они содержат, взаимозаменяемыми (в определенной степени). Это позволяет нам делать то, что мы называем подстановкой : вообще говоря, использовать экземпляр определенного типа как экземпляр другого, связанного типа (например, использовать a String
как an Object
).
Примитивные переменные не взаимозаменяемы одинаково ни друг с другом, ни сObject
. Наиболее очевидная причина (но не единственная) - разница в размерах. Это делает примитивные типы неудобными в этом отношении, но они все еще нужны нам в языке (по причинам, которые в основном сводятся к производительности).
Дженерики и стирание типов
Универсальные типы - это типы с одним или несколькими параметрами типа (точное число называется универсальной арностью ). Например, определение универсального типа List<T>
имеет параметр типа T
, который может быть Object
(создание конкретного типа List<Object>
), String
( List<String>
), Integer
( List<Integer>
) и так далее.
Универсальные типы намного сложнее неуниверсальных. Когда они были представлены Java (после ее первоначального выпуска), чтобы избежать радикальных изменений JVM и возможного нарушения совместимости со старыми двоичными файлами, создатели Java решили реализовать универсальные типы наименее инвазивным способом: все конкретные типы List<T>
на самом деле скомпилированы в (двоичный эквивалент) List<Object>
(для других типов граница может быть чем-то другим Object
, но вы понимаете). В этом процессе теряется общая информация о параметрах арности и типа , поэтому мы называем это стиранием типа .
Соединение двух вместе
Теперь проблема заключается в сочетании перечисленных выше реалий: если List<T>
становится List<Object>
во всех случаях, то T
всегда должен быть тип, которому можно напрямую присвоитьObject
. Ничего другого нельзя допускать. Поскольку, как мы уже говорили ранее, int
, float
и double
не являются взаимозаменяемыми Object
, может не быть List<int>
, List<float>
илиList<double>
(если значительно более сложная реализация дженериков не существовало в JVM).
Но Java типы таких предложений Integer
, Float
и Double
которые обернуть эти примитивы в экземпляры класса, что делает их эффективно взаимозаменяемы , как Object
, таким образом , позволяя общие типы , косвенно работы с примитивами , как хорошо (потому что вы можете иметь List<Integer>
, List<Float>
,List<Double>
и так далее).
Процесс создания Integer
из int
a, Float
a float
и т. Д. Называется боксом . Обратное называется распаковкой . Поскольку необходимость упаковывать примитивы каждый раз, когда вы хотите их использовать Object
, неудобно, бывают случаи, когда язык делает это автоматически - это называется автобоксингом .