Back

Java record pattern matching in JDK 19

145 points10 hoursopenjdk.java.net
mabbo5 hours ago

It's not the feature that excites me, it's the pace of Java language improvements. The 6-month cycle of versions, the whole process of letting experimental features in for a couple versions to test, refine, and likely accept, it's all really working, you know? We're getting stuff that's been so long desired, and it's happening quickly enough to be exciting but not so fast that we're overwhelmed and things are breaking.

People often say that Java 8 was the big important update to the language (streams, lambdas, java.time, and more), but I think Java 9, which began this process was the true rebirth of the language.

quantified59 minutes ago

Interested in your enthusiasm on this front. If (x instanceof String s) was demonstrated at the JavaOne announcing Java 9. The process is proceeding and visible, but finally getting things so long desired and so long available in other languages isn’t supporting much notion of improved pace. Visibility, yes.

hn_throwaway_999 hours ago

Cool to see Java getting these features. After having been a Java dev most of my life, and then moving to Typescript years ago, it's hard to believe I went so long without this.

Related, Typescript is the only language I know of that through flow analysis does not require you to redefine the variable. E.g. you can do something like

  if (typeof foo === 'string') {
    //Typescript knows foo is a string here
  }
All the other languages I know of, like this proposal for Java, require the variable to be redefined. I personally find the Typescript way, as VSCode will give you appropriate type warnings in the right places, easier to use in practice. Just curious if there are any other languages that do it like TS.
SemanticStrengh8 hours ago

Don't be mistaken, Typescript do not has proper pattern matching support, The proposal is far from being adopted https://github.com/tc39/proposal-pattern-matching

> Related, Typescript is the only language I know of that through flow analysis does not require you to redefine the variable. E.g. you can do something like

This is called smart casting and is widely used in Kotlin

viewfromafar7 hours ago

The PL crowd calls that type system feature "occurrence typing."

SemanticStrengh7 hours ago

yes or flow typing https://en.wikipedia.org/wiki/Flow-sensitive_typing It probably overlap with the research on gradual typing too

swaranga9 hours ago

There are some edge cases because of which this had to be done, I think. For example calling static methods via the instance variables, the actual method called would be the static type of the variable at compile time and not the actual type at runtime:

    public static void main(String[] args) {
        Parent instance = new Child();

        if (instance instanceof Child p) {
            instance.print(); // prints Parent
            p.print(); // prints Child
        }
    }

    static class Parent {
        static void print() {
            System.out.println("Parent");
        }
    }

    static class Child extends Parent {
        static void print() {
            System.out.println("Child");
        }
    }
Hence, with flow typing, existing code could break in subtle ways.
smarks2 hours ago

Several folks have pointed out the poor style in calling static methods using an instance. I think you have a good point, but not a good example. Perhaps a better example is with overload resolution. Consider this Kotlin code:

    fun foo(i: Int) = ...

    fun foo(n: Number) = ...

    fun main() {
        val n: Number = 12;
        foo(n); // calls foo(Number)
        if (n is Int) {
            foo(n); // calls foo(Int)
        }
    }
It's as if smart casting causes the static type of `n` to change in different parts of the main function. As such it also seems to affect overload resolution. Maybe this is exactly what you want. On the other hand it seems like it could lead to some very subtle errors. I'm not a Kotlin programmer, so Kotlin experts please feel free to correct me on details.
hilbertseries3 hours ago

If you’re writing code calling static methods on instances of classes, you deserve to have that code broken.

quantified57 minutes ago

Probably didn’t get written that way but arrived there when that class was refactored somehow.

recursive2 hours ago

I've always wondered why that's even allowed.

yarg3 hours ago

Does java provide warnings against calling static methods on Objects?

smarks3 hours ago

Yes, if javac is given the `-Xlint:static` option.

SemanticStrengh8 hours ago

How does Kotlin solve those use cases?

> instance.print(); // prints Parent

why? the parent print should be shadowed/overidden by the child bruh

spullara8 hours ago

statics don't override

SemanticStrengh8 hours ago

oops my bad

moomin7 hours ago

Refinement typing (the TS way) is relatively common in type systems that sit on top of dynamic languages because basically they have no choice. Binding to a new variable, however, is a heck of a lot easier to analyse so tends to be done in languages that don’t need to implement refinement typing.

A couple of interesting exceptions: explicit interfaces make it impractical in C#, and there’s a project called liquid Haskell that adds refinement typing on top of an already strong type system.

saghm6 hours ago

In my compilers class, this is how we did static downcasting for the toy language we created (I think it was called Oat?). I think it's clever, but I find that I prefer mechanisms that rebind the variable for clarity purposes. It's super easy IMO to accidentally gloss over something as being just a regular if statement rather than something that's actually statically changing the type of the variable compared to having different syntax than just a regular conditional. I also tend to prefer being able to look at a variable's usage and then look back to the time it was initialized and know that it's still the same type; having the type change only within a given scope without any explicit binding seems like something I'd mess up a lot, especially when reviewing code I didn't write myself.

pkulak8 hours ago

Kotlin does that as well, and it's a JVM language.

dwaite7 hours ago

Some of this is a consequence of how typescript works - it carries any structural constraints it can find forward in an advisory capacity, because it is somewhat decoupled from the quite-dynamic runtime behavior of the javascript engine.

It isn't even block-based scoping in the flow analysis above - if in your block there was the code foo = 1; the typescript engine would then expect that foo will behave like a Number at runtime.

I haven't dived in deep enough, but I suspect foo could even have different structural type information within the same expression, e.g. a ternary (typeof foo === 'string') ? something(foo) : somethingelse(foo)

arthurcolle4 hours ago

> All the other languages I know of, like this proposal for Java, require the variable to be redefined.

can you give a few examples? This makes no sense to me and I've touched quite a few ecosystems in my time. Maybe I am misunderstanding but my current understanding makes this seem not realistic

danachow8 hours ago

That’s not dataflow analysis. It looks like a cast similar to something like in C# ie: if (foo is string as foo) where the “as foo” scoped variable is implicit. It’s basically implicitly redefining the variable with limited scope. Type guards are a slick feature but it is just an expression match and some syntactic sugar. Even user defined type guards in typescript have to be explicitly declared as such.

This would be more an example of CFA.

  const a = “string”;
  if (typeof v === a)
hn_throwaway_997 hours ago

It's not just a simple cast, but I should have used the correct term which is "control flow analysis". It's not just redefining the variable, control flow analysis works in lots of different places besides just typeof checks, and is possible in many cases because TS is structurally typed, not nominally typed.

papercrane8 hours ago

The Typescript way would only work for local variables in Java. Since another thread might update a field between the instanceof check and the use of the field.

peterkelly2 hours ago

This is an issue in TypeScript even with single-threaded code; it (always/sometimes?) assumes function calls won't affect the results of type narrowing tests: https://github.com/microsoft/TypeScript/issues/9998

zarzavat7 hours ago

You’re definitely holding it wrong if you have polymorphic fields in your lock-free shared memory. Or at least holding it weirdly.

gizmo6867 hours ago

Thats the problem with language design. If the language allows something, it needs to correctly support it; even if actually doing it is a bad idea.

SemanticStrengh8 hours ago

those cases are rare. Defensive copying should be optin, not the default. Java really deep copy objects for even the most basic instanceof checks??

kaba07 hours ago

You can’t change the object’s type in Java, so if you hold on to the same reference it will be safe to use after the instanceof check.

Squarex9 hours ago

Kotlin is the same as typescript.

jung_j9 hours ago

Java 14 does have something similar

https://openjdk.java.net/jeps/305

hn_throwaway_999 hours ago

No, Java requires you to redefine the variable, i.e.

  if (obj instanceof String s) {
    // s is a String here, but obj is not
  }
Pet_Ant9 hours ago

That I would say still counts as “similar” not “the same”

danachow8 hours ago

That’s the same, just that the scoped definition is implicit.

+1
hn_throwaway_997 hours ago
SemanticStrengh8 hours ago

but is it a deep copy??

sebazzz8 hours ago

It is not a copy. It still references the same object.

Smaug1239 hours ago

F# also has:

  let foo : obj = failwith ""

  match foo with
  | :? SomeType as blah ->
      // use blah : SomeType
(You're allowed to just use the identifier `foo` in the match arm.)
halfmatthalfcat7 hours ago

As does Scala:

  match thing {
    case a: String => 
      // use a as string
    case a @ MyCaseClass(b: String, c, d) =>
      // use a as the instance of MyCaseClass or use b as a string
  }
PartiallyTyped8 hours ago

Mypy (Python) does this as well. VSCode and PyCharm provide typehints after

   if isinstance(foo, str):
        # code
dehrmann5 hours ago

Sometimes. If foo isn't local scope, this won't work because it could be modified in another thread.

TheMatten7 hours ago

And of course, Haskell has equivalent too:

    case eqT @a @String of
      Just Refl -> ...
skybrian8 hours ago

Dart also does this.

markdog128 hours ago
quilombodigital8 hours ago

I am an old school java developer. Can someone explain me why this is a "feature"? I can remember by counting my fingers the times I had to use "instanceof", and it was some classical reflection/injection/instantiation framework 'vodu'. If you are using instanceof in a normal java program, you are just making it wrong. It looks like some new javascript guys arrived in the block and are trying to make java look something messy like Scala. What real world problem is this trying to solve? Are you just fucking bored because you are all out of ideas and every JDK release we need "something"? Why these JEPs only have "boxes,rectangles,A,B,I" class examples and not a simple real world class scenario? Why we need to make java "friendly" to work with generic objects? it should be hell! I cant wait for JDK 50.

peterkelly1 hour ago

Pattern matching is a key feature of functional programming languages; it predates Java by 20 years and remains widely used today. See https://en.wikipedia.org/wiki/ML_(programming_language)

For some examples relating to business logic, see https://docs.microsoft.com/en-us/dotnet/csharp/fundamentals/...

Lots of compilers are written in functional languages and use pattern matching for traversing abstract syntax trees, dispatching on the node type. The OO way to do this is to put all logic relating to a particular class in the class itself. The functional approach is to put all logic for a given method/function in one place and handle all the different types. They are two different philosophies both with different tradeoffs, and which is best depends on the type of program you're writing. But it's nice for a language to support both, so you can pick and choose which approach is best for a given problem.

BlackFly7 hours ago

It shows the majority of its strength in switch statements. You can mostly just use the instanceof for a one line destructuring of a compound object into its constituents. The corresponding rust statement is the `if let` or `if case let` if you are more of a swift fan. Generally you would use it with sum types which were previously extremely nasty to work with in Java but are more useful now with sealed classes.

The pattern shows up a lot with result and option types but you can do it any other time where a tagged union makes sense. The classic way to do a tagged union in Java was just a personal taste amongst bad options: tagging interface and instanceof on implementations or an encapsulating class that will return null (or throw an exception) if the gets are called for the types which aren't encapsulated.

If in your career you never needed to enumerate cases that required different data in each case and you couldn't see how binding those requirements into an invariant via the type system was helpful then you probably won't use these new features either. Some people thought it was helpful but the boilerplate cost was too high so wouldn't do it in practice (I am in this category).

Edit: you could also make tagged unions more type safe with callbacks, but then implementing a closure to pull the value out of the callback was just annoying.

quilombodigital6 hours ago

"If in your career you never needed to enumerate cases that required different data..." . Maybe one or two times... but it is not like "hey, it is impossible", or "shit, I have to write 10 more lines" in this rare code I will never put my eyes again. What I get angry is that people will start to use everywhere, in places they should not be using. because the guy did not wanted to stop five minutes to think about it. The river flows through the easiest path, but this does not means it is the smartest path.

Twisol6 hours ago

Unfortunately, I've seen plenty of Java 8 code where people did not "stop five minutes to think about it". I don't think any language can protect us from ourselves.

It's fair that you've found sensible ways to achieve your goals without ever needing pattern matching. Nobody should fault you for that. But can you grant that other developers might have other sensible ways to achieve other goals (or sometimes other ways to achieve the same goals!)?

I think most Java developers will be familiar with the visitor pattern. In almost all cases (there are exceptions!), I detest it; I find sum types with pattern matching to be a far superior way to say what I mean. Java added `sealed` interfaces recently, so you can actually model a closed family of types. Now pattern matching closes the loop, giving you a supremely ergonomic way of dispatching over that closed family without using a visitor.

Maybe it's not clear from other responses, but the part of the JEP about an "exhaustive switch" is critical here. Java statically guarantees that you've handled each member of your closed family, just like if you forgot to implement a method for a visitor.

nchi38 hours ago

Because there are scenarios where pattern matching (or instanceof) is the better and more ergonomic thing to use. One obvious example I can think of is event handling.

If anything, this is _not_ something JS devs are asking for, but rather devs using functional languages.

quilombodigital7 hours ago

Hum... maybe this is the reason my events usually have an abstract base class with an eventype enum field...

misja1117 hours ago

Exactly, this is what OP was trying to point out. With the record matching feature, you don't need the extra abstract base class anymore.

This is not to say that abstract base classes are never a good idea, but sometimes in Java they were only there for the convenience of one handler method, to prevent the use of instanceof. This didn't improve readability. That's when the record matching can be a good improvement.

grumpyprole7 hours ago

Sometimes it isn't desirable to scatter the logic across many classes, or conflate the data and the handling. The Java world has the "visitor pattern" to help deal with that, but its double-dispatch is clunky, complex and verbose. Pattern matching just generalises switch and makes it more useful. Java is genuinely a better language with this change.

Fordec8 hours ago

I also think of API ergonomics. Unspecified input with the same single endpoint, while under the hood will be absolute spaghetti, from a developer adoption standpoint can be make or break.

And if you're strongly typing your system rather than relying on strings, extra so.

halfmatthalfcat8 hours ago

"...look something messy like Scala", I feel bad you feel this way as Scala is an incredibly powerful and elegant language.

It's ironic because you're throwing shade at Javascript when JS developers are doing the same thing against Typescript. Fearing what you don't know or understand under the guise that change is bad or things are "good enough".

quilombodigital7 hours ago

I agree Scala is very powerful. I said messy because it is "too powerful", added too many features. I think this is their mistake. I respect the community, but can show my opinion as you can about java.

kaba04 hours ago

I really don’t feel that Scala has too many features. It is a relatively small language which is very elegant due to having almost no edge cases. Sure, due to not having many features yet being very expressive it has to have very powerful language primitives that can be abused, but I really don’t think that it is as bad as its name.

dopidopHN7 hours ago

I really liked scala… until I had a to maintain a large and « old » code base in it.

I think messy fits.

dboreham6 hours ago

"Write-only language"

grumpyprole7 hours ago

Java is also a messy language and the reason for a lot of the mess in Scala. If you want elegance, then you should probably learn something like Haskell.

+1
quilombodigital6 hours ago
wiseowise7 hours ago

> show my opinion as you can about java.

Showing your opinion doesn't make it immune from critique.

quilombodigital6 hours ago

true

mathgladiator8 hours ago

I wish they had better examples, so here is one. Suppose you are making a scene graph, and you want to have a visitor and run code based on the type of the nodes in the graph. Well, consider two approaches. The first approach is to use a bunch of ifs and instanceof, but this isn't clean and it is fragile. The second approach is to make an interface that has all the types listed under a handler (void handle(Type1 t), void handle(Type2 t), ...) and then write some boiler plate code to do the dispatch.

This feature aims at the conciseness of first model (instanceof) with the safety of the second approach (i.e. all types are handled).

I would love this feature for my project! ( https://www.adama-platform.com ) since I use Java to implement a programming language.

quilombodigital8 hours ago

This is the point, you dont consider the instanceof approach, is just wrong. You already know your types in advance.

jayd168 hours ago

The sealed interface, final implementation case is an interesting one to inspect. Now you can, for example, write a parser with a fixed set of tokens and write switches that exhaustively handle every token. You now get compile time guarantees.

quilombodigital8 hours ago

ok... so you save a few lines in a parser. program your language for the 0.005% developers

kaba07 hours ago

You really could do away with the arrogance.

quilombodigital6 hours ago

I dont think saving a few lines for a very rare corner case is a strong reason to add a language feature

+1
jstimpfle6 hours ago
jayd165 hours ago

It's just an example...

And you don't seem to understand that it's about the safety of the exhaustive check not just the sugar.

jstimpfle6 hours ago

I've never been a fan of Java due to the GC and the boilerplate and runtime overhead (time but also space) that come with it. But I've always had respect for it because it felt so minimal and consistent in its own way, bulky but solid kinda like a BMW car.

Now that functional stuff and instanceof fluff is something else. I don't like it either.

kaba03 hours ago

Unless you only prefer low-level languages, Java’s runtime is anything but bulky. It is one of the fastests runtimes out there, so runtime overhead is relative.

jstimpfle2 hours ago

The runtime might be an efficient implementation of the language, but the language itself (given GC and still no custom value types AFAIK) implies a distinct bulkiness compared to what I am used to. This is as I said my personal feeling when using the language, and is based on the number of keystrokes I've needed in the past to get from A to B, as well as the runtime overhead (memory overhead for many small objects, add in GC and chances are it's very noticeable time-wise too).

It much depends on the task of course. My biggest project in Java was a SAT solver years ago, and millions of little clauses did not sit well with it at all. I ended up changing the data organization internally. My first attempt, the natural AOS would have been also the best choice performance-wise in C, but in Java it choked the GC. I had to switch it around to SOA which was quite awkward but it reduced the number of objects from millions to a small constant.

Also thinking of the number of lines of code running for JIT, GC, whatever... in the background all the time...

valcron10006 hours ago

100% agree. Using 'instance of' in Java is a terrible anti-pattern, same as trying to do pattern matching. You should lift that code into instance methods and let the runtime pick the right implementation.

Fellshard5 hours ago

There are some cases when it's actually simpler or more maintainable or more useful to do the dispatch locally. A language that allows you to both is tougher to learn and can have a higher risk of pitfalls, but also means that a seasoned practitioner can apply the right tool in the right situation.

In terms of the classic 'patterns', it's the difference between standard dispatch and the 'Visitor' pattern. There are cases where you want to specify each case of data structure locally, and others where you want to specify each method of each variant locally. Welcome to the expression problem!

There are cases in Java where you do want a true POJO - just a bag of structured data. This is probably a very strong candidate for using along side that.

peterkelly2 hours ago

The downside to that is if you want to write a new function that deals different subclasses, you can't keep all the logic in one place; you have to spray its implementation across all the different types it has to handle. This is the #1 reason I prefer languages with sum types and pattern matching (or the ability to emulate it via unions and instanceof checks, e.g. TypeScript or Python + mypy).

Frost1x6 hours ago

Highly compositional design patterns in Java. When you have a fairly large set of custom types that are hierarchically structued in nature, it can be quite useful.

In most standard IT applications it may be less common. I've seen it used fairly well in scientific applications. There are of course other ways of approaching working with and acting on wide taxonomies of objects that could exist but this is one of them.

equalsione6 hours ago

> If you are using instanceof in a normal java program

In applications yes, but instanceof is heavily used if you write frameworks.

And as someone else has mentioned, the more functional style java you write the more use you start to make of instanceof

rr8083 hours ago

I kinda feel the same. Modern Java is written like some Python/JS Mashup. Its one reason old code bases are a huge mess because code is written in many different styles.

metadat8 hours ago

This doesn't take anything away, it just adds extra convenience and flexibility.

quilombodigital8 hours ago

My problem is with the "just adds"... Scala made this mistake, and ended with many different ways to do the same task. It is a powerful language, but I believe languages must have only one (or two) concise way to to things.

adra7 hours ago

If you wanted the idiomatic only-one-way-to-do-things approach to do things, Java is not and has never been the language of choice for that. Why do we have 10 different dependency injection /logging / web frameworks? Who cares! Let's add an 11th for good measure. I don't want java to be idiomatic and purist conformant tedium.

You're welcome to switch your language of choice to go if that's really your bag.

quilombodigital7 hours ago

The examples you gave are not language features, are frameworks/libraries... not the discussion. (the only exception I can see is the "logging API", but this is debatable)

halfmatthalfcat7 hours ago

The "multiple ways of doing things" is a feature, not a bug and something which lives in almost every programming language. It's become a meme at this point and a lazy argument against a language.

+1
metadat7 hours ago
Banana69929 minutes ago

This is obviously false in full generality, C++ has many (M A N Y) ways to do anything, and I have never seen even the most fanatic C++ fans defend this as a good thing. Nearly every single C++ dev hates some (large) subset of the language, which subset is another matter entirely, but a subset nonetheless.

Bjarne Stroustrup once noted in Design & Evolution of C++ that people want different things from the seemingly simple artifacts we call "Programming Languages". Some people want an algorithmic language to express procedures cleanly in, others want a design language to express large scale systems in. Some want a terse and uniform "executable mathematics" notation, others want a down-to-earth worse-is-better lets-make-some-goddamn-apps-and-put-some-fucking-bread-on-the-table working class language. Some want a language with an emphasis on the lone author, others an emphasis on the corporate business team. A language as a tool that you wield skillfully for the single purpose it was built for, a language as a toolbox full of gadgets, a language as a material to build the previous two from, a language as a community hub to organize around, etc etc etc. I have seen people argue that "Language" the wrong metaphor to understand computer notations entirely.

This is why you can't say anything general about programming languages.

wiseowise7 hours ago

> but I believe languages must have only one (or two) concise way to to things.

Java has none.

quilombodigital7 hours ago

I humbly accept the critic. :)

kaba07 hours ago

instanceof is but one possible use case. Arguably the most useful part of pattern matching is inside switch expressions, and there is an equivalence between the Visitor pattern and this, but the latter is much more readable and maintainable. Sure, the visitor pattern is not that often used, but in certain niches that make use of ASTs often it is pretty much a must.

hztar8 hours ago

you sound like a Cobol developer I once knew.. RIP

quilombodigital8 hours ago

This is true.... Old guys know nothing... for years we told the javascript guys the mess they were doing, and voila, all dynamic languages suddenly started to convert into statically-typed, and at the end, they have this webassembly, that runs in a schizophrenia-type VM wannabe. Congrats. You lost 10 years of your life because you didnt listen to us when we said that we have learned something from Self and Smalltalk.

Skinney6 hours ago

> and voila, all dynamic languages suddenly started to convert into statically-typed

This is... just wrong.

Plenty of people are using JS without any sort of typing whatsoever. Other dynamic languages, like Clojure, Gradle, Elixir, Pharao (a smalltalk), just to mention a few, are still going strong.

> they have this webassembly, that runs in a schizophrenia-type VM wannabe

wasm is mostly being used as a target for C(++), Rust and even C# programs that want to run in the browser. It's there to expand the reach of the browser. Most JS projects don't touch the stuff.

jstimpfle6 hours ago

> wasm is mostly being used as a target for C(++)

because... JS isn't good enough?

wiseowise6 hours ago

> This is true.... Old guys know nothing... for years we told the Java guys the mess they were doing, and voila, all OOP languages suddenly started to move to functional paradigm. Congrats. You lost 10 years of your life because you didnt listen to us when we said that we have learned something from ML and Scheme.

quilombodigital3 hours ago

good point... but I was not talking about OOP, I was talking about dynamic typing versus static typing. :)

peterkelly2 hours ago

A great paper on this topic:

D. J. Pearce, “Sound and Complete Flow Typing with Unions, Intersections and Negations,” in Verification, Model Checking, and Abstract Interpretation, vol. 7737, R. Giacobazzi, J. Berdine, and I. Mastroeni, Eds. Berlin, Heidelberg: Springer Berlin Heidelberg, 2013, pp. 335–354. doi: 10.1007/978-3-642-35873-9_21.

https://ecs.wgtn.ac.nz/foswiki/pub/Main/TechnicalReportSerie...

blacksmithgu5 hours ago

I love seeing modern language features coming to Java! Sadly, I suspect it will be at least a decade before I see Java 19 used anywhere I work...

cutler7 hours ago

Despite recent improvements including raw string literals it beggars belief how Java still requires regex metacharacters to be escaped. Until that's fixed Java is not an option for me.

Strs2FillMyDrms7 hours ago

For everyone saying the visitor pattern requieres switch statements this is false. The visitor can build its case inside the iterface/implemetation. If the complexity is being solved by the "housing" class that the visitor will be visiting you are not doing enough.

zelphirkalt9 hours ago

Another feature, that Java finally adopts, after decades of not adopting it. More and more language features and concepts are being introduced, finally exposing Java-only programmers to them. Lambdas, pattern matching, project Loom, at some point in the future, Java might be called a modern language. Good for Java and Java programmers.

Just checked some other languages:

- SRFI (Scheme Request for Implementation): https://srfi.schemers.org/srfi-200/srfi-200.html

Syntax:

> | ($ record-name pat_1 ... pat_n) a record

- GNU Guile: https://www.gnu.org/software/guile/manual/html_node/Pattern-...

Seems to be implementing the SRFI:

> | ($ record-name pat_1 ... pat_n) a record

- Rust: https://doc.rust-lang.org/book/ch18-03-pattern-syntax.html and https://doc.rust-lang.org/reference/patterns.html#struct-pat...

> We can also use patterns to destructure structs, enums, and tuples to use different parts of these values. Let’s walk through each value.

> Struct patterns match struct values that match all criteria defined by its subpatterns. They are also used to destructure a struct.

pron8 hours ago

> Another feature, that Java finally adopts, after decades of not adopting it.

Back in 1997, James Gosling published an article that serves as a blueprint to Java's evolution to this day: https://www.win.tue.nl/~evink/education/avp/pdf/feel-of-java... He explained it further, as related by Brian Goetz in the first 20 minutes of this talk: https://www.youtube.com/watch?v=Dq2WQuWVrgQ

In short, Java attempts to be "a wolf in sheep's clothing," with a state-of-the-art, innovative runtime, wrapped in a language that's intentionally conservative. Java the language only adopts features that have already proven themselves as worthwhile elsewhere, and when mainstream programmers are ready for them. We don't always live up to that standard, but we try.

So another way of putting what you said is that this is another feature that has proven itself enough, for long enough, and that's matured enough for Java to adopt it. Many if not most programming language features don't make it to that stage.

The flip side is to examine which features have never made it to Java and probably never will (at least, they're not on our long-term roadmap). Those include, among others, macros, extension methods, first-class properties, and async/await.

SemanticStrengh8 hours ago

there is nothing to be proud of about lacking extension methods. Kotlin has them and it allow to make amazing APIs that are much more ergonomic/reduce cognitive overhead.

pron8 hours ago

I personally think there's much to be proud of in lacking any feature, as long as you deliver what your users expect, especially if the number of users is very large. After all, the goal of a mainstream programming language is not to have as many features as possible, but as few as necessary, where "necessary" takes into account both the common software requirements of the era (which are also shaped by the hardware characteristics of the era) as well as the expectations and habits of the majority of programmers at that point in time.

I can tell you that if Java's language team encounters some language feature that solves a particular problem, they'd rather spend spend several years thinking how to avoid adding that feature while still addressing the problem, than the six months needed to implement it. A good strategy for doing that is to wait, and try to think of ways to solve multiple problems with one feature (even if imperfectly) rather than solve multiple problems with multiple features.

_old_dude_7 hours ago

The same feature does not entail the same tradeoff depending on the languages.

For Kotlin, extensions methods are essential otherwise you can not extend existing Java types with Kotlin types.

For C#, you need extension methods for LINQ because modifying the .Net runtime was not an option at that time.

For Java, changing the VM is not an issue, instead of extension methods you add default methods which can be overridden by implementations.

Same feature, different tradeoffs.

kaba07 hours ago

I think Kotlin went the easy way with extension methods. Scala’s solution is much more elegant with implicits (especially now with Scala 3).

SemanticStrengh6 hours ago

I'd be very interested in a comparison between scala 3 using/given and Kotlin new context receivers https://github.com/Kotlin/KEEP/blob/master/proposals/context...

grishka8 hours ago

What I love about Java though, is that this new syntax is very clear and its intentions are obvious when you're reading someone else's code. It doesn't require you to hold a mountain of context in your head at all times like Swift and Kotlin do. Yes, it's verbose at times. But verbosity is a good property for a programming language because it allows the code to be read and understood with ease outside of an IDE.

jeroenhd4 hours ago

Most Java is very easy to comprehend, but then you have things like implicit finals and whatever you call the `List<? extends Something>` language construct. I think most Java is very boring, functional code with some notable exceptions related to threading and the typing system. It gets the job done, but often in an ugly, roundabout way.

The way `Optional` is implemented and the roundabout way to just grab the first item in a list (`list.stream().findFirst()` or `var e = null; if (!list.isEmpty()) e = list.get(0);`?) where other languages have added helpers years ago. Or, even worse, the lack of tuples, leading to your average medium-sized Java project containing five different implementations of Tuple/Pair. There are also runtime restrictions that sometimes crop up because Oracle can't break compatibility (type erasure, for example).

If code clarity was the only metric for language quality then we'd all be writing some BASIC derivative. I don't think more modern languages like Swift and Kotlin are all that applicable to all areas where Java shines, but ever since dotnet went open source and cross platform I'm really starting to wonder why anyone still bothers with Java.

vips7L1 hour ago

I personally feel that tuples are an antipattern. It’s relatively simple to just define a record with the properties you need and gives meaningful context information because the record and it’s properties are named.

Jach7 hours ago

I was almost with you up until the end then I had to just shake my head. I mean, it's readable syntax and doesn't require some crazy new sigils or operators, it's "fine" though I'm also shaking my head at the scope rules for these bindings. Similarly I'm ok with the "fine" syntax of Optional, and being explicit with my Optional.orElse()'s, rather than introducing an 'elvis operator' or some bikeshedded derivative. Though I wouldn't mind such things -- I'm glad Java keeps evolving useful things anyway, but it does wear on you (or at least me) to have to speak them (or even to tell your IDE to speak them on your behalf) as if your mouth was full of sand or to read them as if they're a high school student's essay obviously padded to reach some word count minimum.

If I came across this new thing organically, I could go "ah, neat, we have that now" and not necessarily need to go look up the JEP (meanwhile Python's PEP 636 had me going wtf) -- though over time I'd expect to come across it in a negative context where the original programmer made a scope mistake because they didn't read the JEPs, or because they tried to make a change without their IDE's assistance where through various means it could have pointed out shadowing or scope concerns, that I now have to fix.

It's in no minor part thanks to Java's verbosity that the overall Java ecosystem is so verbose. Having mostly non-confusing syntax whose meaning you can mostly guess at on first exposure is nice, yes, but isn't so helpful for the more important aspect of having non-confusing programs. For that you really want a more concise and tasteful language over a verbose one. Anyway, I've found that for anything non-trivial about the program itself, I'm going to need an IDE because the meat of the thing is going to be verbosely spread out in many places (sometimes for good reasons, at least in the Java world). Sure, after I acquire the mountain of (program) context, I can review small changes even on paper printouts, but that's true of most anything.

Verbosity is not a good property for a language to have in general -- Java itself admits this, otherwise we wouldn't have so many shortcuts in syntax like omitting this, or java 7's diamond operator or try-with-resources or catching multiple exceptions or for-each syntax, or java 8's lambdas and special syntax for simple lambdas, or java 9's var, or... And of course, almost always in some other more concise language a concept is much clearer. There's a reason pseudo-code isn't written to look like Java.

elric9 hours ago

> Another feature, that Java finally adopts, after decades of not adopting it

Java has been steadily moving in this direction for quite a few years now. It's tricky business. There's the language proper, and there's the JVM, which are interdependent. Both are aiming to remain backwards compatible. Introducing new major language features is not an easy feat.

pavelbr6 hours ago

C# has been adding new major language features all the time since its inception, including pretty much everything we are seeing being added to Java these days.

Skinney6 hours ago

Yeah, and this "kitchen sink" approach is partly why I stopped writing C#. Same thing is turning me away from JS.

It's exhausting trying to stay up-to-date with the language, in addition to changes in the frameworks and tools on top of the challenges of my day job.

I might be showing my age, but I've come to appreciate that features come after long and careful consideration, and not every single release.

eecc6 hours ago

Cool, thanks Java gatekeepers for procrastinating your change of mind long enough to save face.

In the meantime I wish we had a community that could evolve at that sweet, optimal pace, a promise that the Javascript one completely blew. Rust?