Это действительно расширение по сравнению с автобоксом?

Я видел это в ответе на другой вопрос , касающийся недостатков спецификации 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)

вывод будет печатать "Целое число"

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

Ответов (3)

Решение

В первом случае происходит расширяющаяся конверсия. Это можно увидеть, запустив служебную программу "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", вы получите следующий код в основном методе:

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), чтобы поместить примитив внутри оболочки.

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

Изменить: ссылка на исходный пост .

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

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

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.