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

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

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

Почему f1 становится null, а второй оператор выдает исключение NullPointerException?

Langspec-3.0 para 15.25 sais:

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

и false?1.0f:null T1-Float, а T2-тип null. Но каков результат lub(T1,T2)? Этот пункт 15.12.2.7 просто немного чересчур ...

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

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

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 внутренний conditional является нулевым типом, а тип третьего операнда f2 внешний условный считается плавающим.

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

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

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

условное f1 и внутреннее условное f2 идентичны:(ложные ? 1.0 f : 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 присвоение является плавающей ссылочной переменной, результат выражения (null) успешно назначен.

для внешнего условного F2:(ложные ? 1.0 f: [F2 внутреннее условное])

для внешнего условного F2 типы являются:

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

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

  • в противном случае, если второй и третий операнды имеют типы, которые могут быть конвертированы (§5.1.8) к числовым типам, тогда там есть несколько случаев: ...

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

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


следующее вызовет 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;

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

    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;

:)

Edit: на самом деле, глядя ближе, кажется, что этот случай на самом деле является смесью между Гамлет (тернарный оператор и обернутые интегральные типы) и Элвис (auto-unboxing null) головоломки. В любом случае, могу только порекомендовать посмотреть видео, оно очень познавательное и приятное.


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

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