Это действительно расширяется против автобокса? - PullRequest
27 голосов
/ 07 августа 2008

Я видел это в ответе на другой вопрос , касающийся недостатков спецификации Java:

Есть и другие недостатки, и это тонкая тема. Проверьте это из:

public class methodOverloading{
     public static void hello(Integer x){
          System.out.println("Integer");
     }

     public static void hello(long x){
          System.out.println("long");
     }

     public static void main(String[] args){
         int i = 5;
         hello(i);
     }
}

Здесь будет напечатано «long» (сам не проверял), потому что компилятор выбирает расширение вместо автобокса. Будьте осторожны при использовании автобокса или вообще не используйте его!

Мы уверены, что это на самом деле пример расширения вместо автобокса, или это что-то совсем другое?

При моем начальном сканировании я бы согласился с утверждением, что вывод будет "длинным" на основе объявления i как примитива, а не объекта. Однако, если вы изменили

hello(long x)

до

hello(Long x)

вывод будет выводить "Integer"

Что на самом деле здесь происходит? Я ничего не знаю о компиляторах / интерпретаторах байт-кода для Java ...

Ответы [ 3 ]

13 голосов
/ 07 августа 2008

В первом случае происходит расширение конверсии. Это можно увидеть при запуске утилиты "javap" (входит в JDK) в скомпилированном классе:

public static void main(java.lang.String[]);
  Code:
   0:   iconst_ 5
   1:   istore_ 1
   2:   iload_ 1
   3:   i2l
   4:   invokestatic    #6; //Method hello:(J)V
   7:   return

}

Очевидно, вы видите I2L, который является мнемоникой для расширяющейся инструкции байт-кода Integer-To-Long. См. Ссылку здесь .

А в другом случае, заменив «long x» на сигнатуру объекта «Long x», вы получите следующий код в методе main:

public static void main(java.lang.String[]);
  Code:
   0:   iconst_ 5
   1:   istore_ 1
   2:   iload_ 1
   3:   invokestatic    #6; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
   6:   invokestatic    #7; //Method hello:(Ljava/lang/Integer;)V
   9:   return

}

Итак, вы видите, что компилятор создал инструкцию Integer.valueOf (int), чтобы поместить примитив в оболочку.

4 голосов
/ 07 августа 2008

Да, попробуйте в тесте. Вы увидите «длинный» напечатанный. Он расширяется, потому что Java выберет расширение int на долгое время, прежде чем оно решит автоматически помещать его в Integer, поэтому выбирается метод hello (long).

Редактировать: исходное сообщение, на которое ссылаются .

Дальнейшее редактирование: причина, по которой второй вариант будет выводить Integer, заключается в том, что в качестве опции нет «расширения» в больший примитив, поэтому он ДОЛЖЕН упаковать его, поэтому Integer является единственной опцией. Кроме того, java будет автоматически блокировать только исходный тип, поэтому он выдаст ошибку компилятора, если вы оставите hello (Long) и удалите hello (Integer).

2 голосов
/ 07 августа 2008

Другая интересная вещь в этом примере - перегрузка метода. Сочетание расширения типов и перегрузки методов работает только потому, что компилятор должен решить, какой метод выбрать. Рассмотрим следующий пример:

public static void hello(Collection x){
   System.out.println("Collection");
}

public static void hello(List x){
   System.out.println("List");
}

public static void main(String[] args){
   Collection col = new ArrayList();
   hello(col);
}

Он не использует тип времени выполнения, который является List, он использует тип времени компиляции, который является Collection, и, таким образом, печатает «Collection».

Я рекомендую вам прочитать Эффективная Java , что открыло мне глаза на некоторые угловые случаи JLS.

...