Условный оператор Java?: тип результата

Я немного озадачен об условном операторе. Рассмотрите следующие две строки:

Float f1 = false? 1.0f: null;
Float f2 = false? 1.0f: false? 1.0f: null;

Почему f1 становится пустым, и второй оператор бросает NullPointerException?

Параграф 15.25 Langspec-3.0 Саис:

Иначе вторые и третьи операнды имеют типы S1 и S2 соответственно. Позвольте T1 быть типом, который следует из применения преобразования упаковки в S1, и позвольте T2 быть типом, который следует из применения преобразования упаковки в S2. Тип условного выражения является результатом применения преобразования получения (§5.1.10) к лабу (T1, T2) (§15.12.2.7).

Таким образом для false?1.0f:null T1 является Плаванием, и T2 является пустым типом. Но каков результат lub(T1,T2)? Этот параграф 15.12.2.7 просто немного слишком много...

BTW, я использую 1.6.0_18 в Windows.

PS: Я знаю это Float f2 = false? (Float) 1.0f: false? (Float) 1.0f: null; не бросает NPE.

36
задан Mechanical snail 24 September 2012 в 21:29
поделиться

5 ответов

Разница заключается в статической типизации выражений во время компиляции:

Резюме

E1: `(false ? 1.0f : null)`
    - arg 2 '1.0f'           : type float,
    - arg 3 'null'           : type null 
    - therefore operator ?:  : type Float (see explanation below)
    - therefore autobox arg2
    - therefore autobox arg3

E2: `(false ? 1.0f : (false ? 1.0f : null))`
    - arg 2 '1.0f'                    : type float
    - arg 3 '(false ? 1.0f : null)'   : type Float (this expr is same as E1)
    - therefore, outer operator ?:    : type float (see explanation below)
    - therefore un-autobox arg3

Подробное объяснение:

Вот что я понял из чтения спецификации и работы в обратном направлении от результата, который вы получили. Все сводится к тому, что тип третьего операнда условия f2 inner - null type, а тип третьего операнда условия f2 outer считается Float.

Примечание: Важно помнить, что определение типа и вставка кода boxing/unboxing выполняется во время компиляции. Фактическое выполнение кода boxing/unboxing происходит во время выполнения.

Float f1 = (false ? 1.0f : null);
Float f2 = (false ? 1.0f : (false ? 1.0f : null));

Условное выражение f1 и внутреннее условное выражение f2: (false ? 1.0f : null)

Условие f1 и внутреннее условие f2 идентичны: (false ? 1.0f : null). Типы операндов в условном f1 и внутреннем f2 следующие:

type of second operand = float
type of third operand = null type (§4.1)

Большинство правил из §15.25 передаются вверх, и эта финальная оценка действительно применяется:

Иначе, второй и третий операнды имеют типы S1 и S2 соответственно. Пусть T1 - это тип, который получается в результате применения боксового преобразования к S1, а T2 - это тип, который получается в результате применения боксового преобразования к S2. Тип условного выражения является результатом применения преобразования захвата (§5.1.10) к lub(T1, T2) (§15.12.2.7).

S1 = float
S2 = null type
T1 = Float
T2 = null type
type of the f1 and f2 inner conditional expressions = Float

Поскольку для f1 присвоение производится ссылочной переменной Float, результат выражения (null) успешно присвоен.

Для f2 внешнее условие: (false ? 1.0f : [внутреннее условие f2])

Для внешнего условия f2 типы:

type of second operand = float
type of third operand = Float

Обратите внимание на разницу в типах операндов по сравнению с внутренними условиями f1/f2, которые ссылаются на литерал null напрямую (§4.1). Из-за этого отличия наличия двух числовых конвертируемых типов применяется правило из §15.12.2.7:

  • Иначе, если второй и третий операнды имеют конвертируемые типы (§5.1. 8) к числовым типам, то возможны несколько случаев: ...

    • В противном случае к типам операндов применяется двоичное числовое продвижение (§5.6.2), и типом условного выражения является продвинутый тип второго и третьего операндов. Обратите внимание, что двоичное числовое продвижение выполняет преобразование без упаковки (§5.1.8) и преобразование набора значений (§5.1.13).

Из-за того, что для результата внутреннего условия f2 (null) было выполнено преобразование unboxing, возникло исключение NullPointerException.

26
ответ дан 27 November 2019 в 06:15
поделиться

Я думаю, что переписывание кода делает объяснение более ясным:

    float f = 1.0f;

    Float null_Float  = false?        f  : null;       // float + null  -> OK
    Float null_Float2 = false? (Float)f  : null_Float; // Float + Float -> OK
    Float npe         = false?        f  : null_Float; // float + Float -> NPE

Таким образом, NPE - это когда мы пытаемся сделать что-то вроде:

Float npe = false? 1.0f : (Float)null;
2
ответ дан 27 November 2019 в 06:15
поделиться

Быть или не быть - вот в чем вопрос. :)

Edit: На самом деле, если присмотреться, кажется, что этот случай на самом деле представляет собой смесь между Hamlet (тернарный оператор и обернутые целочисленные типы) и Elvis (автоматическая распаковка нуля) головоломки. В любом случае могу только рекомендовать посмотреть видео, оно очень познавательное и приятное.

2
ответ дан 27 November 2019 в 06:15
поделиться

Следующий пример вызовет ошибку NPE, поскольку вы пытаетесь присвоить null примитиву

    float f1 = false ? 1.0f: null;

Я полагаю, что именно это и вызывает ошибку NPE во втором операторе. Поскольку первая тройка возвращает float для true, она также пытается преобразовать false в float.

Первый оператор не будет конвертировать в null, так как требуемый результат - Float

Это, например, не вызовет NPE, так как больше не нужно конвертировать в примитив

    Float f = false? new Float(1.0f): true ? null : 1.0f;
2
ответ дан 27 November 2019 в 06:15
поделиться

Похоже, что JVM пытается распаковывать второе значение null в float вместо Float, таким образом, NullPointerException. Ударил его сам один раз. Я так считает, что вторая часть if делает это, потому что часть true первой if оценивается как float, а не Float.

Подумав, я думаю, что это способ Java сказать вам, что вы делаете что-то странное. Только не гнездите троичные если, и у вас все будет хорошо :-)

1
ответ дан 27 November 2019 в 06:15
поделиться
Другие вопросы по тегам:

Похожие вопросы: