Является ли кастинг из числа в два раза допустимым в Java 7? (Autoboxing) - программирование

Является ли кастинг из числа в два раза допустимым в Java 7? (Autoboxing)

Коллега проверена в этом коде:

    Number n = ...;
    double nr = n == null ? 0.0 : (double) n;

Другой коллега тогда пожаловался, что это не скомпилировалось, и что бы я ожидал. Однако оказалось, что я уже вытащил этот код из SVN, и все сработало нормально. У всех нас была версия Java, равная 1,7 в eclipse, и оказалось, что код компилируется под eclipse 4.4.2 (Luna), но не работает под 4.2.2.

Я исправил проблему, заменив листинг на n.doubleValue().

Теперь актуальный вопрос: почему это принято в первую очередь? Разумеется, он должен работать при кастомизации Double вместо Double, но я думаю, что прямая трансляция из Number в Double была запрещена. Итак, является ли это ошибкой в ​​eclipse 4.2.2, которая была исправлена ​​в то же время, или же eclipse 4.4.2 молча принимает код, который не должен компилироваться (что будет ИМХО быть ошибкой)?

4b9b3361

Ответ 1

С Java 7 система литья должна была слегка измениться в отношении примитивных типов, чтобы разрешить работать с MethodHandle s, При вызове дескриптора метода компилятор javac генерирует так называемую полиморфную подпись, которая происходит из сигнатур метода метода. Эти полиморфные сигнатуры создаются путем указания типа параметра с литьем. Например, при связывании метода с сигнатурой double, long -> int требуется следующее литье:

Number foo = 42d, bar = 43L;
int ignored = (int) methodHandle.invoke((double) object, (long) bar);

Подпись исходного кода MethodHandle::invoke, однако, определяется как Object[] -> Object, без непосредственного внесения значения в примитивный тип, полиморфная подпись не может быть сгенерирована.

Очевидно, что для того, чтобы это было возможно, компилятор Java должен был быть изменен, чтобы позволить такие отливки, которые ранее не были законными. Хотя теоретически возможно ограничить это использование отливок методами, которые аннотируются с помощью @PolymorhicSignature, это привело бы к странному исключению, почему в настоящее время это вообще возможно в javac, где соответствующий байт-код генерируется, когда не создается полиморфная подпись. Тем не менее, примитивные типы по-прежнему представляют собой собственные типы времени выполнения, о чем было указано в другом ответе, в котором размещен сгенерированный байт-код такой отливки вне MethodHandle

Object foo = 42;
int.class.cast(foo);

приведет к исключению во время выполнения.

Однако я согласен с комментариями, что это не обязательно должно быть должным образом рассмотрено в JLS, но я нашел нить, упоминающую этот разрыв спецификации. Упоминается, что спецификация должна быть обновлена ​​соответствующим образом, как только появятся лямбда-выражения, но JLS для Java 8, похоже, не упоминает такие отливки или @PolymorphicSignature. В то же время он утверждает, что [a] ny-преобразование, которое явно не разрешено, запрещено.

Возможно, JLS в настоящее время отстает от реализации javac, и компилятор Eclipse, безусловно, не выбрал это правильно. Вы можете сравнить это с некоторыми краевыми случаями вывода типового типа, где несколько компиляторов IDE ведут себя по-другому, чем javac до сегодняшнего дня.

Ответ 2

Как указано в комментариях, номер не имеет соответствующего примитивного типа, но компилятор Java кажется умным, чтобы выполнить преобразование под капотом после того, как вы положили его.

Вот пример тестового примера

пакетный тест;

открытый класс NumberAutoboxing {

public static void main(String[] args) {
    Number n =new Long(1);
    double nr = (double) n;
    Integer i= 1;//boxing
    Integer j= new Integer(1);
    int k=j;//unboxing
    System.out.print("n="+n+" nr=" + nr + " i="+ i + " k=" + k);
}

}

Я декомпилировал .class(я тестировал jdk 7 и 8 на windows, и результат тот же), и это результат

import java.io.PrintStream;

public class NumberAutoboxing
{
  public static void main(String[] args)
  {
    Number n = new Long(1L);
    double nr = ((Double)n).doubleValue();
    Integer i = Integer.valueOf(1);
    Integer j = new Integer(1);
    int k = j.intValue();
    System.out.print("n=" + n + " nr=" + nr + " i=" + i + " k=" + k);
  }
}

Как вы можете видеть, преобразование из числа в double производится таким же образом, как и в примере unboxing (от Integer до int); , потому что компилятор изменил приведение от double до double и, таким образом, разрешил распаковку. Кажется, что были две фации компиляции (или что-то подобное, что я размышляю над этим)

  • понимая, что, поскольку n - число, значение акта должно измениться double to Double
  • распаковка

Ответ 3

Абстрактный класс Number - это суперкласс классов BigDecimal, BigInteger, Byte, Double, Float, Integer, Long и Short.

Double - это класс-оболочка для двойки и поддержка автоматического бокса, но не числа. В других словах Число может содержать много классов-оболочек, которые не являются двойными, поэтому вам нужно явное литье. Попробуйте этот код:

Number n = 78.3145;
double nr = (double) n;
Double d = 3.1788;
double dr = d;
System.out.println(n);
System.out.println(dr);