Сведения о вопросе

PASHA

16:03, 1st July, 2020

Теги

java   primitive   autoboxing    

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

Просмотров: 429   Ответов: 3

Я видел это в ответе на другой вопрос, в отношении недостатков спецификации 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" будет напечатан (не проверял его сам), потому что компилятор выбирает расширение вместо автоматического бокса. Будьте осторожны при использовании автоматического бокса или не используйте его вообще!

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

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

hello(long x)

к

hello(Long x)

вывод будет печатать "Integer"

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



  Сведения об ответе

VCe znayu

18:03, 1st July, 2020

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


  Сведения об ответе

darknet

18:03, 1st July, 2020

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

Редактировать: ссылка на исходную запись .

Дальнейшее редактирование: причина, по которой второй вариант будет печатать целое число, заключается в том, что нет "widening" в большем примитиве в качестве опции, поэтому он MUST вставляет его, таким образом, Integer является единственным вариантом. Кроме того, java будет только autobox к исходному типу, поэтому он даст ошибку компилятора, если вы оставите hello(Long) и удалите hello(Integer).


  Сведения об ответе

padenie

18:03, 1st July, 2020

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

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);
}

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

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


Ответить на вопрос

Чтобы ответить на вопрос вам нужно войти в систему или зарегистрироваться