[patterns] Destructuring without matching?

Tagir Valeev amaembo at gmail.com
Fri Jul 7 18:51:32 UTC 2017

Argh, those nulls. Well, they gradually becoming non-grata in Java, so
probably it would be good to throw NPE in case if null appears at
unconditional deconstruction. After all when method called with
(Integer)null while expects primitive int, you'll got an NPE even though
you have no explicit dereference. Unboxing is pretty similar to
deconstruction, actually deconstruction of Integer would work like
unboxing, so such behavior looks consistent to me.

With best regards,
Tagir Valeev.

7 июля 2017 г. 6:35 PM пользователь "Brian Goetz" <brian.goetz at oracle.com>

On 7/7/2017 11:21 AM, Tagir Valeev wrote:

> Hello!
> Sometimes it's reasonable to deconstruct the object of known type. A
> classical example is Map.Entry. E.g. currently I can write:
> // Map<String, Integer> wordCounts
> wordCounts.entrySet().stream()
>    .filter(e -> e.getValue() > 10)
>    .map(e -> e.getKey()+": "+e.getValue())
>    .forEach(System.out::println);
> It's desired to refer by concrete name to key and value like this:
> wordCounts.entrySet().stream()
>    .filter((_, count) -> count > 10)
>    .map((word, count) -> word+": "+count)
>    .forEach(System.out::println);
> Of course such syntax is not possible. Reading pattern matching proposal I
> thought whether it could solve this somehow. I see these alternatives:
> wordCounts.entrySet().stream()
>    .filter(e -> e matches Entry(_, count) && count > 10)
>    .map(e -> e matches Entry(word, count) ? word+": "+count : null)
>    .forEach(System.out::println);
> Or
> wordCounts.entrySet().stream()
>    .filter(e -> exprswitch(e) {case Entry(_, count) -> count > 10; default
> -> false;})
>    .map(e -> exprswitch(e) {case Entry(word, count) -> word+": "+count;
> default -> null;})
>    .forEach(System.out::println);
> In the latter case default branch is redundant as we know that e is always
> an Entry. Probably compiler could figure out this as well and do not
> require default branch? In this case exprswitch will have only one branch
> and looks too verbose.

Not so fast!  We know that its static type is Entry, but it might be null.
In which case it would be foolish to try to let it match Entry(var w, var
c) -- because once we cast to Entry, we'll NPE immediately when we try to
extract the word/count components.  (See the recent writeup on patterns and
nullity on amber-spec-experts.) Without nullity information in the type
system, that pesky default branch is actually needed.

More information about the amber-dev mailing list