Finalizing in JDK 16 - Pattern matching for instanceof
Fred Toussi
fredt at users.sourceforge.net
Mon Jul 27 12:32:27 UTC 2020
The proposed scoping rule, which allows what you call the "swiss cheese property" goes against existing Java rules, is a recipe for future bugs, and should not be allowed.
> // field p is in scope
>
> if (e instanceof Point p) {
> // p refers to the pattern variable
> } else {
> // p refers to the field
> }
>
Compare with the existing scoping rule for inner block variables and "for" variables, which does not allow a declared field to be re-declared in inner blocks.
int val = 0;
{
int val = 1; // not allowed
}
for (int val = 0; val < 2; val++) { // not allowed
}
Fred
On Mon, Jul 27, 2020, at 11:53, Gavin Bierman wrote:
> In JDK 16 we are planning to finalize two JEPs:
>
> - Pattern matching for `instanceof`
> - Records
>
> Whilst we don't have any major open issues for either of these features, I would
> like us to close them out. So I thought it would be useful to quickly summarize
> the features and the issues that have arisen over the preview periods so far. In
> this email I will discuss pattern matching; a following email will cover the
> Records feature.
>
> Pattern matching
> ----------------
>
> Adding conditional pattern matching to an expression form is the main technical
> novelty of our design of this feature. There are several advantages that come
> from this targeting of an expression form: First, we get to refactor a very
> common programming pattern:
>
> if (e instanceof T) {
> T t = (T)e; // grr...
> ...
> }
>
> to
>
> if (e instanceof T t) {
> // let the pattern matching do the work!
> ...
> }
>
> A second, less obvious advantage is that we can combine the pattern matching
> instanceof with other *expressions*. This enables us to compactly express things
> with expressions that are unnecessarily complicated using statements. For
> example, when implementing a class Point, we might write an equals method as
> follows:
>
> public boolean equals(Object o) {
> if (!(o instanceof Point))
> return false;
> Point other = (Point) o;
> return x == other.x
> && y == other.y;
> }
>
> Using pattern matching with instanceof instead, we can combine this into a
> single expression, eliminating the repetition and simplifying the control flow:
>
> public boolean equals(Object o) {
> return (o instanceof Point other)
> && x == other.x
> && y == other.y;
> }
>
> The conditionality of pattern matching - if a value does not match a pattern,
> then the pattern variable is not bound - means that we have to consider
> carefully the scope of the pattern variable. We could do something simple and
> say that the scope of the pattern variable is the containing statement and all
> subsequent statements in the enclosing block. But this has unfortunate
> 'poisoning' consequences, e.g.
>
> if (a instanceof Point p) {
> ...
> }
> if (b instanceof Point p) { // ERROR - p is in scope
> ...
> }
>
> In other words in the second statement the pattern variable is in a poisoned
> state - it is in scope, but it should not be accessible as it may not be
> instantiated with a value. Moreover, as it is in scope, we can't declare it
> again. This means that a pattern variable is 'poisoned' after it is declared, so
> the pattern-loving programmer will have to think of lots of distinct names for
> their pattern variables.
>
> We have chosen another way: Java already uses flow analysis - both in checking
> the access of local variables and blank final fields, and detecting unreachable
> statements. We lean on this concept to introduce the new notion of flow scoping.
> A pattern variable is only in scope where the compiler can deduce that the
> pattern has matched and the variable will be bound. This analysis is flow
> sensitive and works in a similar way to the existing analyses. Returning to our
> example:
>
> if (a instanceof Point p) {
> // p is in scope
> ...
> }
> // p not in scope here
> if (b instanceof Point p) { // Sure!
> ...
> }
>
> The motto is "a pattern variable is in scope where it has definitely matched".
> This is intuitive, allows for the safe reuse of pattern variables, and Java
> developers are already used to flow sensitive analyses.
>
> As pattern variables are treated in all other respects like normal variables
> -- and this was an important design principle -- they can shadow fields.
> However, their flow scoping nature means that some care must be taken to
> determine whether a name refers to a pattern variable declaration shadowing a
> field declaration or a field declaration.
>
> // field p is in scope
>
> if (e instanceof Point p) {
> // p refers to the pattern variable
> } else {
> // p refers to the field
> }
>
> We call this unfortunate interaction of flow scoping and shadowing the "Swiss
> cheese property". To rule it out would require ad-hoc special cases or more
> features, and our sense is that will not be that common, so we have decided to
> keep the feature simple. We hope that IDEs will quickly come to help programmers
> who have difficulty with flow scoping and shadowing.
More information about the amber-spec-observers
mailing list