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

StackOverflow https://stackoverflow.com/questions/4922

  •  08-06-2019
  •  | 
  •  

Вопрос

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

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

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

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

hello(long x)

к

hello(Long x)

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

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

Это было полезно?

Решение

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

А в другом случае, заменив «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 до long, прежде чем автоматически упаковать его в Integer, поэтому для вызова выбирается метод hello(long).

Редактировать: исходный пост, на который есть ссылка.

Дальнейшее редактирование:Причина, по которой второй вариант будет печатать Integer, заключается в том, что в качестве опции нет «расширения» в более крупный примитив, поэтому он ДОЛЖЕН упаковать его, поэтому Integer является единственным вариантом.Более того, Java автоматически упаковывает только исходный тип, поэтому компилятор выдаст ошибку, если вы оставите hello(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.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top