[patterns] Destructuring without matching?

Brian Goetz brian.goetz at oracle.com
Fri Jul 7 19:42:32 UTC 2017


I think it would be pretty unreasonable for

     if (x matches Point(var x, var y)) { ... }

to NPE, which is basically what you're arguing for.  Failure to match 
should result in a boolean false, not an exception.



On 7/7/2017 2:51 PM, Tagir Valeev wrote:
> 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 <mailto: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