Seems you’re asking “why have static patterns, when all patterns have a target 
anyway”.

Let’s back up and ask “what couldn’t we do if Java didn’t have static methods.” 
 There are a number of things we could not express:

 - Methods whose arguments and return types are primitives, such as Math.max(). 
 We cannot recast these as constructor or instance methods because primitives 
do not have these.  We’d have to instead express these as instance methods on 
boxes.  
 - Static factories.  While we could live in a world without static factories, 
it is a useful pattern, and EJ rightly tells us why we should prefer them to 
constructors.  Without static methods, we’d just have to use constructors.
 - “Outboard” functionality.  If String didn’t provide a reverse() method, you 
could write your own: MyStringUtils.reverse(String).  

In some cases, we have no choice between instance and static, but in many 
cases, we do; either choice for reversing a string would have been acceptable. 
This is one of the degrees of freedom that Java developers are used to, and 
have learned to navigate.  

While there are obviously other ways to get “static” functionality without the 
keyword “static” (e.g., Scala’s “object” idiom), I think we can agree these are 
all tools we find useful, even if static-ness has lots of warts and sharp 
edges.  

One of the cornerstones of the design proposed for patterns is that 
destructuring is the dual of aggregation, and that how we express destructuring 
should have similar syntax and semantics to how we express aggregation.  If we 
express aggregation with a constructor, we should have a anti-constructor-like 
pattern:

    Foo f = new Foo(x);
    if (f instanceof Foo(var x)) { … }

(This duality is not mere syntactic trick; it appeals to a powerful 
mathematical concept called _adjunction_, and the consequences of this are, in 
turn,  an enabler for things like reconstruction.)  

If we express aggregation with a factory, though, we would like a 
“unfactory”-like notion of destructuring: 

    Foo f = Foo.of(x);
    if (f instanceof Foo.of(var x)) { … }

Could we get away with saying that this is unnecessary?  Well, we could allow 
static factories but not static patterns, but for all the reasons Josh says 
“prefer factories to constructors”, we can make many of the same arguments for 
static patterns.  Switching over an Optional this way would suck:

    case Optional(var value):
    case Optional(): // empty

There’s a reason we decided not to expose constructors for Optional shaped like 
this.  

Additionally, as Tagir points out, static methods are powerful for letting us 
add “extension” functionality.  If Foo doesn’t provide a constructor for a 
given configuration of Foo, but that configuration is otherwise constructible 
anyway, anyone can write a factory for “makeFooWithCheeseAndMustard”.  If you 
can write a factory to aggregate, you may reasonably want to write a 
corresponding pattern to destructure.  


> One problem is that while it's obvious that the first pattern starts by 
> checking if o is an instanceof String,
> it's far less clear from a user POV that the second pattern does exactly the 
> same thing and does not check if o is an instance of Integer.

I think you’re asking: how does a user know that a pattern is applicable to the 
target?  And the answer is: just like methods, we look at the declaration.  For 
instance patterns, the target is the same type as the class its declared in; 
for static patterns, the type is explicit in the declaration.  

Reply via email to