2015-05-08 1 views
12

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

+0

Это не то, что позволяет JLS. –

+2

@PeterLawrey: Я тоже этого не ожидал, но javac 1.7 на Linux, похоже, разрешает это. Интересно. –

+0

Он должен работать, если вы сделаете это 'double nr = n == null? 0.0: (Double) n; ' –

ответ

6

С Java 7 система литья должна была слегка измениться в отношении примитивных типов, чтобы разрешить работу с MethodHandle. При вызове дескриптора метода компилятор javac генерирует so-called polymorhic signature, который получается из сигнатур метода метода.Эти полиморфные сигнатуры создаются путем указания типа параметра с литьем. Например, при связывании метод с подписью 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, но я found a thread mentioning this specification gap. Упоминается, что спецификация должна быть обновлена ​​соответствующим образом после того, как появятся лямбда-выражения, но JLS для Java 8, похоже, не упоминает такие отливки или @PolymorphicSignature. В то же время в нем указывается, что [a] ny конверсия, которая явно не разрешена, запрещена.

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

-1

Относительный класс 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); 
+0

Вторая строка - это то, что я написал, и это принято по одной версии компилятора, но отклоненной другой. Когда я прочитал JLS, это должно быть ошибкой. Что заставляет меня думать, хотя и то, что оба eclipse 4.4.2 и javac принимают его. – Axel

+0

Это ОФИЦИАЛЬНАЯ ДОКУМЕНТАЦИЯ https: // docs .oracle.com/javase/7/docs/api/java/lang/Number.html Число - суперкласс классов BigDecimal, BigInter, Byte, Double, Float, Integer, Long и Short. и "** Autoboxing и unboxing ** позволяет использовать примитив interchan в качестве своего объекта ** класс-оболочка ** "(E. Finegan & R. Liguori - OCA Java SE7 pag.151). Поэтому, если у вас есть компилятор, который компилирует Number как класс-оболочку, это, безусловно: 1) неправильная практика кодирования 2) проблема с компилятором. Пожалуйста, используйте документацию, если хотите что-то утверждать. –

+0

Да, похоже, им пришлось сменить компилятор и забыл обновить документацию. См. Принятый ответ и ссылки для подробностей. PS: Я не downvoter ... ;-) – Axel

0

Как указано в поле Число комментариев не имеет соответствующего примитивного типа, но компилятор 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 на окнах и результат тот же), и это результат

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 и таким образом разрешил распаковывать. Кажется, что там было два comilation fases (или что-то подобное я спекулируют на этом)

  1. понимая, что с п число литая должно измениться от двойной Удвоить
  2. распаковка
+0

Проблема с «компилятором Java кажется разумной для выполнения преобразования под капотом» - это предложение из JLS: «Любое преобразование, которое явно не разрешено, запрещено». – Axel

+0

@Axel моя фраза лучше объясняется после; есть два преобразования сначала от двойного до двойного, а затем автобоксинг, и каждый из них разрешен; Я думаю, что в этом случае JLS должен быть более подробным – Giovanni

Смежные вопросы