complex `?:` case

29 views
Skip to first unread message

zhong...@gmail.com

unread,
Aug 12, 2015, 12:46:36 PM8/12/15
to java.lang.fans
Question from SO - http://stackoverflow.com/questions/31970213

My would-be answer :

----

Conditional operator `?:` has always been quite complicated, see [jls7].

Now it reaches a new height of incomprehensibility, see [jls8]. This is mostly because they want "target typing" to propagate inside.

My understanding is that,

        false ? `boolean` : null

is a "standalone", "reference conditional expression"; its type is Boolean.

And

        false ? `boolean` : `Boolean`

is a "boolean" conditional expression, and according to the big table in jls8, the type of this expression is primitive `boolean`. Therefore unboxing must be performed on the 3rd operand.

[jls7](http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.25)
[jls8](http://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.25)

y s

unread,
Aug 12, 2015, 1:31:56 PM8/12/15
to Zhong Yu, java.lang.fans
Easy enough to test. :)

    public class Tern {
      public static void main(String[] args) {
        runSafe("primitive, null", () -> { boolean b = false ? true : null; });
        runSafe("Object, null", () -> { Object b = false ? true : null; });
      }

      public static void runSafe(String id, Runnable r) {
        System.out.println("running " + id);
        try { r.run(); System.out.println("Succeeded!"); }
        catch (Exception e) { e.printStackTrace(); }
      }
    }

(Don't worry, I'd never format my code like this for real. :) Just reducing vertical space for email.) As expected, this reports an exception for the first runSafe and success for the second. But even better evidence is found via javap:

    private static void lambda$main$0();
      Code:
         0: aconst_null
         1: checkcast     #18                 // class java/lang/Boolean
         4: invokevirtual #19                 // Method java/lang/Boolean.booleanValue:()Z
         7: istore_0
         8: return

    private static void lambda$main$1();
      Code:
         0: aconst_null
         1: astore_0
         2: return

So, yup. That first lambda is constant-folded to ((Boolean)null).booleanValue(), which unsurprisingly throws an NPE.

Personally, I think anyone who writes (false ? false : false ? false : null) deserves what they get ;)

--
You received this message because you are subscribed to the Google Groups "java.lang.fans" group.
To unsubscribe from this group and stop receiving emails from it, send an email to java-lang-fan...@googlegroups.com.
To post to this group, send email to java-la...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/java-lang-fans/4350246d-d853-486d-879e-30c7f9268ce6%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Zhong Yu

unread,
Aug 12, 2015, 2:11:11 PM8/12/15
to y s, java.lang.fans
On Wed, Aug 12, 2015 at 12:31 PM, y s <eleus...@gmail.com> wrote:
>
> Personally, I think anyone who writes (false ? false : false ? false : null) deserves what they get ;)


Certainly. But OP also gave a more realistic, legit use case

    Function<String, Boolean> f = s -> s.equals("0") ? false : s.equals("1") ? true : null;

Here `Boolean` is probably intended to represent a 3-value type; unfortunately the `null` case blows up.

It's better not to mix primitive and reference types; and personally I'd narrow `null` to intended type too. So my fix would be

    s.equals("0") ? Boolean.FALSE : s.equals("1") ? Boolean.TRUE : (Boolean)null

there's no question the intended type is `Boolean` throughout.

Zhong Yu

y s

unread,
Aug 12, 2015, 2:34:19 PM8/12/15
to Zhong Yu, java.lang.fans
It's a bit of an OT tangent, but I actually don't like chained ternaries anyway. I can never remember the order of precedence, and even if I could, it's visually unclear. In fact, for anything but the very shortest of expressions, I split it into three lines:

    int whatever = someCondition
        ? calculateSomething()
        : calculateSomethingElse();

On top of that, co-opting Boolean for a tri-value is a bit sketchy. You can mostly make a case for it where the null represents "unknown" (e.g., a boolean ratioIsPositive where the denominator is 0), though even then I'd argue for deciding whether "unknown" resolves to true or false as quickly as possible, and ideally at the time when you first evaluate it (in the ratioIsPositive example, I'd expect that a denominator of 0 means false, since undefined is not positive; and if you want it to be true, call the var ratioIsPostiveOrUndefined or similar). Or, barring that, creating a tri-value enum (POSTIVE, NEGATIVE, UNDEFINED). Note for instance that Boolean.valueOf(String) returns false, not null, if you pass in a null.

Point is, Boolean is really there to provide a two-value class that can also be used with collections/generics/etc. The fact that this lets it technically become a three-value class is an unfortunate side effect, not something to be exploited.

So I still think the OP got what they deserve. ;)

zhong...@gmail.com

unread,
Oct 16, 2015, 10:34:08 PM10/16/15
to java.lang.fans
Reply all
Reply to author
Forward
0 new messages