[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