Автобокс понятен всем Java-разработчикам, начиная с Java 1.5. Ну, я могу быть слишком оптимистичным. По крайней мере, все разработчики должны быть в порядке с автобоксом. Ведь на странице ORACLE есть хороший учебник по этому поводу.
Автобокс — это явление, когда компилятор Java автоматически генерирует код, создавая объект из примитивного типа, когда это необходимо. Например, вы можете написать:
1
|
Integer a = 42 ; |
и он автоматически сгенерирует код JVM, который помещает значение int
42 в объект Integer
. Это так мило со стороны компилятора, что он делает это для нас, что через некоторое время мы, программисты, просто забываем о сложности, стоящей за ним, и время от времени мы сталкиваемся со стеной.
Например, у нас есть double.class
и Double.class
. Оба они являются объектами (как класс, и каждый класс сам по себе является объектом в permgen или просто в куче в пост-пермгенской версии JVM). Оба эти объекта имеют тип Class
. Более того: начиная с Java 1.5 оба они имеют тип Class<Double>
.
Если два объекта имеют одинаковый тип, они также должны быть совместимы по присваиванию, не так ли. Кажется, очевидное утверждение. Если у вас есть объект O a
и объект O b
тогда вы можете назначить a = b
.
Глядя на код, мы можем понять, что забываем, а не видим:
1
2
3
4
5
6
7
8
9
|
public class TypeFun { public static void main(String[] args) { // public static final Class<Double> TYPE = (Class<Double>)Class.getPrimitiveClass("double"); System.out.println( "Double.TYPE == double.class: " + (Double.TYPE == double . class )); System.out.println( "Double.TYPE == Double.class: " + (Double.TYPE == Double. class )); System.out.println( "double.class.isAssignableFrom(Double.class): " + ( double . class .isAssignableFrom(Double. class ))); System.out.println( "Double.class.isAssignableFrom(double.class): " + (Double. class .isAssignableFrom( double . class ))); } } |
в результате чего:
1
2
3
4
|
Double.TYPE == double.class: true Double.TYPE == Double.class: false double.class.isAssignableFrom(Double.class): false Double.class.isAssignableFrom(double.class): false |
Это означает, что примитивная пара Double
является double.class
(не удивительно). Даже если одно не может быть назначено от другого. Мы можем взглянуть на источник хотя бы одного из них. Источник класса Double
находится в RT.jar и является открытым исходным кодом. Там вы можете увидеть, что:
1
|
public static final Class<Double> TYPE = (Class<Double>) Class.getPrimitiveClass( "double" ); |
Почему он использует этот странный Class.getPrimitiveClass("double")
вместо double.class
? Это пара примитивов типа Double
.
Ответ не тривиален, и вы можете углубиться в детали Java и JVM. Поскольку double
— это не класс, в действительности нет ничего лучше double.class
. Вы по-прежнему можете использовать этот литерал в исходном коде Java, и именно здесь язык Java, компилятор и среда выполнения имеют сильную зависимость. Компилятор знает, что класс Double
определяет поле с именем TYPE
обозначающее его примитивный тип. Всякий раз, когда компилятор видит double.class
в исходном коде, он генерирует код JVM Double.TYPE
(попробуйте и затем используйте javap для декодирования сгенерированного кода!). Именно по этой причине разработчик РТ не мог написать:
1
|
public static final Class<Double> TYPE = double . class ; |
в источник класса Double
. Это скомпилируется в эквивалент кода:
1
|
public static final Class<Double> TYPE = TYPE; |
Как происходит автобокс? Источник:
1
|
Double b = ( double ) 1.0 ; |
полученные результаты:
1
2
3
|
0: dconst_1 1: invokestatic #2 // Method java/lang/Double.valueOf:(D)Ljava/lang/Double; 4: astore_1 |
однако, если мы заменим две буквы «d»:
1
|
double b = (Double) 1.0 ; |
тогда мы получим:
1
2
3
4
|
0: dconst_1 1: invokestatic #2 // Method java/lang/Double.valueOf:(D)Ljava/lang/Double; 4: invokevirtual #3 // Method java/lang/Double.doubleValue:()D 7: dstore_1 |
Который объясняет многое. Экземпляры класса double.class
и класса Double.class
не совместимы по назначению. Автобокс решает это. Java 4 была давным-давно, и мы, к счастью, забыли об этом.
Ваша домашняя работа: перечитайте то, что происходит с автобоксом, когда у вас перегружены методы с аргументами типа «класс» и соответствующего типа примитива.