I couldn't agree more. I learn so much from these series. It's such a great way to stay up to date with the latest features of Java. Kudos to Nicolai Parlog.
Been doing Java since the 1.2 but discovered this channel only now... Gotta say, pleasantly surprised by the quantity and quality of the content here... You people did an amazing job with this series, hopefully more to come for the next releases... Thanks to all those who contributed to it!!
You did such a great job not only displaying Java features but also packaging them in a nice and meaningful concept and feeding it to us. You really display awareness of how it is useful to have compilation errors over runtime errors, i.e. failing fast. You also display how a design pattern became a language feature and how to harvest that feature. What is great is that this knowlegde applies to any other language as well, speaking generally in terms of concepts. Thank you!
Pattern matching now is almost perfect as Rust. Also if there is an enum optional it can be used with switch to handle all case and avoid NPE. Better if possible to handle such enum typed only with switch thus always restricting to handle all cases. As current enums can be handled even without switch.
Sorry to hear that. They're about 20-25dB more quiet than my voice, which should be sufficient to allow the voice to stand out, but I understand that personal preferences differ. I hope you could still follow along. And if there are issues, keep in mind that all my videos have handcrafted subtitles.
@@sillystuff6247Sure. That's the beauty of TH-cam, there's something for everybody's taste. Maybe you want to check out Jose Paumard's JEP Cafe's on this channel. There's music in those, too, but people usually don't find it distracting.
@@nipafxfor me the problem with the music is not dB but frequency and beats, I think its a sensory overload thing. The content of the video is excellent otherwise.
Had to wait whole 6 years and 11 java versions to get pattern matching, had been already using it in Kotlin. Not to mention Scala... Glad that Java people are taking note of other languages and concepts, but this is still very slow adoption rate despite the 6 month release cycle. I am much more excited for Green Threads and Structured Concurrency in Java. I encourage people to explore other languages. You can grow a beard and go bald with Java's speed of introducing new features to the language. And mention of lambda expressions introduced in Java 8 which was released over 9 years ago... made me chuckle.
Thanks! You mean the digital ones, right? (Because my IRL wallpaper is pretty cool, too. 😁) I just search for "Sci fi city" or "cyberpunk city" and pick what looks good.
Hey @java team, got a question about exhaustiveness. Say we have module A which declares a sealed interface that module B uses on a switch expectation. For some reason module A evolves and declares a new implementation for the sealed type but module B is not recompiled, hence not exhaustive. There is still a check performed behind the scenes that throws an error right?
Yes, of course. 😃 It would be as if you added a default branch that throws an exception (I forgot which one exactly it is, right now). And what you observed implies that changing the `permits` clause of a type is a breaking change.
@@nipafx Thanks for the answer. Not sure I would consider it a breaking change as I'm adding new types, not changing existing semantics. But this is just a though exercise since bumping dependencies usually requires recompilation.
While in our code there are only few 'instanceof', I see a lot more use-cases for its destructuring feature! But why does it need an if() and is not supported directly in the method declaration, like for example in TypeScript?
17:28 - There is probably a good reason for it - but I just can't make it out yet: Why got a new keyword introduced ("when") instead of simply using "if"?
For now I try to remember/differenciate it like this: "if" is always the first word of a line and used like a method (with round brackets); "when" is used in the middle of a line and doesn't require round brackets; // But maybe there is a better way to put it and differenciate "when" from "if"
Thank you for explaining each feature in such depth. I am bit confused on "Arbitrary Check" or "Express the symmetry" scenario, if someone can please help me with this.
Please improve the voice quality in video and lower the volume of music... this is happening in all the videos and it makes very hard to listen the statements
Exciting but also scary how many features Java has and it keeps growing. I fear at some point it will be hard for one person to actually have all of them in mind and also hard for new engineers to get to learn Java. In my opinion, many mentioned features do not really add anything significant to the language other than more room for opinions. Why is it necessary to be able to do the same task in 5 different ways?
I don't get it - we're decoupling dispatch from method calls? Isn't that just multimethods? Seems like a lot of boilerplate to do that. I never got the record type thing, either... aren't those just beans with no setters defined and complete constructors? Can't you do that with lombok?
Multimethods are dispatched based on their argument type, right? Java's common, inheritance-based dynamic dispatch doesn't do that. By switching over the argument type as presented here you *can* do that but I wouldn't describe these patterns as actually doing it. In essence, the video argues for `call(foo)` as an alternative to `foo.call()`, so the only polymorphism is in `foo`, which is essentially the receiver. Records are not beans - records are transparent carriers for immutable data. Aren't those the same? No, beans are usually created in a broken state (no-args constructor) and then fields are set individually without a good option to verify whether their arrangement of specific values works. This is particularly problematic when beans are deserialized (from bytes, JSON, XML, etc.) because construction does not actually use a constructor. And the compiler doesn't know what a bean is, so it can't take them apart - unlike records with record patterns (and future constructs that will build on it). Re Lombok: No, those data classes don't have record patterns. Also, not every project wants to depend on a tool that uses internal JDK APIs and is thus a potential migration hazard.
I mostly agree with this take. I realy fail to see value provided in this example. The salaried/freelancer employee made sense only in a sense that if you choose to model your business requirement as Java classes, by hardcoding requirements to subtypes, than yes pattern matching is kinda nice. With compiler type checks, etc. But I still fail to see reason for modeling code in that way..This example wouldn't save a line of code compared to procedural else-if in Java 1.4: if ("salaried".equals(employee.type) paySalary(employee); else if ("freelancer".equals(employee.type) payInvoice(employee); People are gonna scream type saftey, but te moment sombody adds "unknown" employee to db table without consulting you, you are dead in water. Been there, done that. The only real type safety is DB column type. I guess compiler dudes really love their vistors. But end of the chain app developers in real world never use it. There is no polymorphic subtyping problem on the third floor of your accounting firm. In general sealed classed and records are really strange turn for Java. Such an arbitary syntax, and ad hoc concepts never to be seen in any other language. This 'when' keyword when if would perfectly nice...
Currently (< java 21 LTS) Lombok is better in every way. Immutable structs w/o some mechanism like lombok's @With are so cumbersome it's actually a step backwards in every respect to DX. Thats even ignoring all the stuff like @Builder However, record is part of the JDK so will be used in its apis like matching and destructuring. record will eventually become a value type, at which point it will have a performance reason to use.
@@bariole If you want to count lines, you should probably include the ones it takes to add and populate a `type` field in every employee implementation. 😉 (And not all data comes from databases.) But this is not about line count, it's about compiler support. If I add a new subtype to a sealed interface, I don't need to jump through hoops to find all the if-else-if chains I need to update - I just follow the compile errors.
@@nipafx In my experience, I've encountered issues every time I've attempted to model user data into Java types. Code with hardcoded types often breaks when a third party submits a value to your app that doesn't map to one of the predefined types or enums. The only solution in such cases is to deploy a "new version." Example of: excessive type enforcement -> nice compiler checks -> production failure. I understand that this video is a presentation of a new language feature. Arguing about the semantics of the example is kinda useless. Nice video though.
Liskov subtype substitution principle is something about what people like to talk, but mostly do not care about. Subtyping is mostly done for code sharing.
The content covered is very good. Unfortunately a lot of extra background noises which is very distracting. Also it would be great of we could show more code than the presenter himself. I was very interested in seeing what he is typing than how he is typing. Thanks.
Why do I have to declare anything at all? Feels like a `case Salaried -> processPayGrade(grade)` should be enough. I would think it's clear that `grade` belongs to the implicit `Salaried` object/record that's in scope for that case statement. Shadowing an existing `grade` variable feels reasonable. What am I missing?
What you describe is flow typing and it works reasonably well for type patterns. But there's no uniform path from there to more complex patterns as, for example, record patterns, for which you need to introduce new variables anyway. So Java went with what it calls flow scoping, which requires new variables (that are in scope everywhere the check is true - hence the name). All that said, there's also a compatibility problem. Imagine an overloaded method `accept(Object)` and `accept(String)`. Today `if (obj instanceof String) { accept(obj); }` calls the first variant but after flow typing is introduced, it's suddenly the latter. That's not good.
@@nipafx i think i'd need to see an example. It doesn't seem so different than just using something like "with" in kotlin; all it does is not make you have to explicitly qualify the member. in practice collisions and the like don't really happen. this feels like the typical "late to the party with a compromised offering" java is infamous for.
@@adambickford8720 I really think flow scoping is more powerful than flow typing and that will become apparent as we get more involved patterns: Now they will all follow the same structure (need to declare a new variable) whereas otherwise we had one special case (every pattern introduces new variables except this one pattern) - I prefer the more uniform approach. That said, it's not really an option anyway because of the incompatibility I pointed out. You have the example right there: It shows how introducing flow typing will change which methods get executed *in existing code*. And what you call "collisions", I call "overloading" and that definitely happens a lot in my code. 😃
@@nipafxHow can there be existing code for a feature that doesn't exist? I'll assume its like records and, someday, they'll have a purpose but are half baked as-is.
There is no code for a feature that doesn't exist - that's the problem! There's just code - and then the new feature comes along and changes what some of it means (I gave you the example: Your proposal would silently change which method overload gets used). Java doesn't do that. And records aren't half-baked, they're exactly what they need to be: transparent carriers for immutable data. And we can already see the transparency paying off with record patterns. (Some people think records exist to avoid boilerplate and that's when you should use them, but they're wrong and using records with that motivation will be very unfulfilling.)
That's true and another reason to organize the program's flow via return values instead of void methods that change some state. But the problem is that you simply don't always have something meaningful to return.
I guess in such a case you may be able to create sealed class hierarchy (i.e. sealed sub-groups and further use sealed types over those sub-groups). Well, it certainly depends on the use case.
Java's new class-file API (JEP draft #8280389) has even more types than that and is entirely data-oriented. As previous commenters said, in such situations you'll very likely have a deeper hierarchy, so most types end up with no more than half a dozen inheriting types.
Before I try to answer that question, keep in mind that, as the video explains, the presented approach is not meant to *replace* OOP but *complement* it. In no way am I trying to say that you should throw OOP out and do this everywhere - instead use pattern matching or data-oriented programming (DOP), where it fits the problem. So OCP will still be a thing in those portions of your code that follow an OOP style. So let's ask "Is OCP a thing in pattern matching/DOP?" And that's not easy to answer. A sealed hierarchy of records is not open to extension by inheritance or adding fields, but it is very open to adding operations. Is that enough to fulfill OCP? I don't know. 😃
Why can’t Java deduce implementation of a class / interfaces based on class’es metadata on compilation time instead of relying sealed classes? In terms of sealed classes with switch exhaustiveness, I think it should also work for non sealed classes too since all implementations of a class / interface can be resolved on compile time. Is there anything I am missing in this regard? I think this can be another useful enhancement
It's actually somewhat uncommon for all implementations of an interface to be compiled at the same time. You have separate compilation every time you extend or implement an interface that doesn't live in the same JAR, e.g. from the JDK, from one of your third-party dependencies, or even from your own code that lives in another subproject. Beyond that, sealed classes also express an *intent*. It's not just that all implementations are known right now. You intend to always know all implementations.
HI @@nipafx, I completely understand the intent part, but even for 3rd part JARs, should the compiler need to compile it and throw a compilation error if there is a switch case with a missing class type of an interface? I am thinking about a more generalized solution for the exhaustiveness of switch statements in terms of interface/class extensions. At the very least, all JAR needs to be compiled at the very end - prior to running the code - and there might be a solution (I bet it is not easy with multiple JARs from all different libraries) to catch all missing cases just before running the app. I just wonder, does the *sealed* introduced have a proper intent, or is it a current solution for the exhaustiveness?
@@Ambusher306 To start at the end: I strongly doubt that there will be other mechanisms than `sealed` to determine exhaustiveness. Before answering the rest of your comment, I need to clarify a something. You wrote "even for 3rd part JARs, should the compiler need to compile it" and "At the very least, all JAR needs to be compiled at the very end" - this seems to imply that there is a conceptual phase between building a JAR and executing the code therein. That is not the case! Bytecode as it gets loaded from a JAR is executed as is and no additional compilation takes place, so there's no step where exhaustiveness could be checked. You might be thinking of just-in-time compilation but that step is purely a performance optimization. It must not have any impact on the semantics of a Java program. Furthermore, it is inherently unqualified to apply the checks you're describing here because it does not compile all extensions of a class or all implementations of an interface at the same time (think about it, if it wanted to do that, it would have to compile every class at once because they all extend `Object` or, if you remove that as a special case, it would still compile a ton of classes because so many implement common interfaces like `Serializable`). But even if there were some step after packaging a JAR that would make a list of implementations and apply that as exhaustiveness checks at run time, it still wouldn't get you anything. At compile time (i.e. when you're writing the code) that list doesn't exist and so the compiler only has two choices: force all switches over non-sealed types to be exhaustive (as it does today) or allow all of them to be non-exhaustive - either way you don't get any meaningful support when it comes to future-proof switches.
most of the things here like this enhanced switch exist in scala for ages only a lot better, i donno why not just "copy" as is. for example there is no need to define "Salary salary ->" as if u are checking the type the "employee" inside that branch is used as Salary. sealed types - kotlin&scala have those for ages Strings as triple quotes exist in scala/kotlin and seems better than the new java way, same with variable plugins "hello $someVar"
I find it already works great in everyday programming, but there are things that can be improved, I agree. Out of curiosity: What do you think should be added?
@@nipafx They plan on adding the ability to extract record parameters into variables, and I believe matching on the values. Once they do this, it will be “real” pattern matching like in Scala and Rust.
@@nipafx I missed that. That is partially what I mean. In more advanced pattern matching you can do: case Salaried(“foo”, 5, 10) case Salaried(“x”, 25, 100) case Salaried(_, _, 1000) I don’t think that is possible with the version demoed here. Though I would be happy it I am wrong.
@@kurzweil4 You want to filter by values right? A la "execute this branch if the `Salaried` instance has name 'foo', etc", right? Brian wrote this about them four years (!) ago: openjdk.org/projects/amber/design-notes/patterns/type-patterns-in-switch#do-we-need-constant-patterns
What specifically do you not like? Are there any issues you foresee? The single underscore was deprecated as a variable name in Java 8 (2014) and forbidden in Java 9 (2017) with this exact purpose in mind. And it's used with the semantics of "unused/unknown/unspecified" in languages like Haskell, Scala, Kotlin, JavaScript, and many more. So it's a well-established concept that proved useful across a wide variety of languages and use cases.
@@nipafx Personally I do not like that you have to declare it in the pattern matching, although it is not used on the right side of the statement. I would have preferred the ability to not write anything at all there. But that is a minor thing to complain about.
Good thing they picked up the pace. Otherwise this would have been released in 2030 😁 If they keep this up maybe some day it would even make sense to use plain Java instead of Kotlin. The only critical problem still present is the null handling. Optional is not an option. Pun intended.
As a past java dev and now a kotlin dev I have one question - why do people still use java if kotlin is like 10 years ahead and allows seamlessly program for jvm?
Java has caught up to Kotlin in most cases, and in some cases gone past like virtual threads. Personally I find Kotlin too bloated with too many ways to do the same thing and too many unnecessary key words. It's slowly follows the path of Javascript, C# and C++ with too much clutter in the language. The Kotlin syntax is also more noisy than Java in my personal opinion and harder to read. For the most part I find server applications written in modern Java (17+ with preview features) easier to maintain than the Kotlin applications in our organization. That beeing said, the programming language is probably the least important thing when it comes to good software design. The skill of the programmer is what matter most. Deep knowledge in application architecture, deep domain knowledge and strong communication skills matters a lot more.
Is it though? Where are it's virtual threads? Where's the deconstruction? Where are multiline strings with indentation detection? Where is string interpolation that includes domain-specific rules? I'm sure I can come up with more... Kotlin may have started 10 years ahead but that's no longer the case and chances are it will evolve similarly to Scala (whose developers have asked similar questions to you up to a few years ago).
Because your premisse is wrong. Please explain to me how is Kotlin better language than Java 8 with Lomobk? Kotlin is mostly syntax sugar atop of Java. At same time it is proprietary technology without support of any of big enterprise shops.
java 21 with lombok is 90% of the way there and certainly not worth the tooling, training, etc challenges. We're a far cry from kotlin vs vanilla java ~8. And it feels like kotlin is slowing down, not speeding up.
The guy clearly knows a lot. But all these neons, background music, camera changes, memorised "speech"... people can't just open an ide and code naturally anymore? Feels like we are in a TV show.
It amazes me how even Java gets really better it still sucks 😆 Looks like it will never reach the ease of use and safety of more recent languages. Sorry Java.
Java 21 brings pattern matching … well . Scala has had it from the begging. Nevertheless . Pattern matching is a functional programming feature which is an anti pattern for classic OOP paradigm . In OOP we use polymorphism instead. Java just ugly and extra verbose. Such elementary feature as pattern matching introduced as a breakthrough while in Scala or Haskell it is just a usual syntax construct
Modern Java works great with functional programming. These days I do way more functional programming than anything else in Java. Functional Java code actually looks pretty similar to Scala in many cases. I find functional programming pluss domain driven design pluss Java's easy to read syntax a match made in heaven in terms of maintainability.
It's a breakthrough *for Java* 😉and it's just usual syntax for it now, too. And if used as explained it *is* polymorphism, so "in OOP we use polymorphism instead" doesn't really make sense. You probably mean dynamic dispatch via inheritance hierarchies, but the video explains in detail why that's not always a good choice.
Yeah but Scala is dead so who cares? And Haskell is not a practical language. No company uses it. It's a fun language to play around with to understand functional programming but there is no job market for Haskell developers.
Creation of this channel is probably next best thing Java people did after introducing 6 month release cadence. Keep up the good work people!
I couldn't agree more. I learn so much from these series. It's such a great way to stay up to date with the latest features of Java. Kudos to Nicolai Parlog.
Been doing Java since the 1.2 but discovered this channel only now... Gotta say, pleasantly surprised by the quantity and quality of the content here... You people did an amazing job with this series, hopefully more to come for the next releases... Thanks to all those who contributed to it!!
I saw a mohawk. I pressed like and subscribe. Nothing beats a mohawk in the corporate world.
😂
You did such a great job not only displaying Java features but also packaging them in a nice and meaningful concept and feeding it to us. You really display awareness of how it is useful to have compilation errors over runtime errors, i.e. failing fast. You also display how a design pattern became a language feature and how to harvest that feature. What is great is that this knowlegde applies to any other language as well, speaking generally in terms of concepts. Thank you!
Okay this is actually legit very cool and useful. Thanks java people
This is HUGE. One of the reasons I love Rust is because of this!
Great effort in the field of Java and great place to find the java update at one place
Thank you. Really enjoyed the Video! I am looking forward to the upgrade
Thank you! :)
for as much as I coud understand what you were explaining ... just brilliant !
Very nice set of new features. Great video!
Thank you for your effort! I like your videos very much!
Such a great and instructive video!
Always enjoy your work Nipafx.
😊
These things are a prime example of why C# and Java competing with each other is fantastic for us.
Pattern matching now is almost perfect as Rust.
Also if there is an enum optional it can be used with switch to handle all case and avoid NPE. Better if possible to handle such enum typed only with switch thus always restricting to handle all cases. As current enums can be handled even without switch.
With this step we came even closer to Lisp. Congrats, everybody!
Thank you for this series. I would love to watch the next bit
Great job everyone. I enjoyed all your videos.
This is just fantastic!
Nice video! Note: the audio tracks are very distracting.
Sorry to hear that. They're about 20-25dB more quiet than my voice, which should be sufficient to allow the voice to stand out, but I understand that personal preferences differ. I hope you could still follow along. And if there are issues, keep in mind that all my videos have handcrafted subtitles.
@@sillystuff6247Sure. That's the beauty of TH-cam, there's something for everybody's taste. Maybe you want to check out Jose Paumard's JEP Cafe's on this channel. There's music in those, too, but people usually don't find it distracting.
@@nipafxfor me the problem with the music is not dB but frequency and beats, I think its a sensory overload thing. The content of the video is excellent otherwise.
great, looking more and more like haskell
Had to wait whole 6 years and 11 java versions to get pattern matching, had been already using it in Kotlin. Not to mention Scala... Glad that Java people are taking note of other languages and concepts, but this is still very slow adoption rate despite the 6 month release cycle. I am much more excited for Green Threads and Structured Concurrency in Java. I encourage people to explore other languages. You can grow a beard and go bald with Java's speed of introducing new features to the language. And mention of lambda expressions introduced in Java 8 which was released over 9 years ago... made me chuckle.
Thanks for nice videos!
I like your wallpapers, where did you get them @nipafx?
Thanks! You mean the digital ones, right? (Because my IRL wallpaper is pretty cool, too. 😁) I just search for "Sci fi city" or "cyberpunk city" and pick what looks good.
Hey @java team, got a question about exhaustiveness.
Say we have module A which declares a sealed interface that module B uses on a switch expectation. For some reason module A evolves and declares a new implementation for the sealed type but module B is not recompiled, hence not exhaustive. There is still a check performed behind the scenes that throws an error right?
Yes, of course. 😃 It would be as if you added a default branch that throws an exception (I forgot which one exactly it is, right now). And what you observed implies that changing the `permits` clause of a type is a breaking change.
@@nipafx Thanks for the answer. Not sure I would consider it a breaking change as I'm adding new types, not changing existing semantics. But this is just a though exercise since bumping dependencies usually requires recompilation.
While in our code there are only few 'instanceof', I see a lot more use-cases for its destructuring feature! But why does it need an if() and is not supported directly in the method declaration, like for example in TypeScript?
17:28 - There is probably a good reason for it - but I just can't make it out yet: Why got a new keyword introduced ("when") instead of simply using "if"?
For now I try to remember/differenciate it like this:
"if" is always the first word of a line and used like a method (with round brackets);
"when" is used in the middle of a line and doesn't require round brackets;
// But maybe there is a better way to put it and differenciate "when" from "if"
Thank you for explaining each feature in such depth. I am bit confused on "Arbitrary Check" or "Express the symmetry" scenario, if someone can please help me with this.
Please improve the voice quality in video and lower the volume of music... this is happening in all the videos and it makes very hard to listen the statements
Exciting but also scary how many features Java has and it keeps growing. I fear at some point it will be hard for one person to actually have all of them in mind and also hard for new engineers to get to learn Java. In my opinion, many mentioned features do not really add anything significant to the language other than more room for opinions. Why is it necessary to be able to do the same task in 5 different ways?
This problem exists in every major old language now, they support every paradigm, and over time have copy pasted features among each other.
I don't get it - we're decoupling dispatch from method calls? Isn't that just multimethods? Seems like a lot of boilerplate to do that. I never got the record type thing, either... aren't those just beans with no setters defined and complete constructors? Can't you do that with lombok?
Multimethods are dispatched based on their argument type, right? Java's common, inheritance-based dynamic dispatch doesn't do that. By switching over the argument type as presented here you *can* do that but I wouldn't describe these patterns as actually doing it. In essence, the video argues for `call(foo)` as an alternative to `foo.call()`, so the only polymorphism is in `foo`, which is essentially the receiver.
Records are not beans - records are transparent carriers for immutable data. Aren't those the same? No, beans are usually created in a broken state (no-args constructor) and then fields are set individually without a good option to verify whether their arrangement of specific values works. This is particularly problematic when beans are deserialized (from bytes, JSON, XML, etc.) because construction does not actually use a constructor. And the compiler doesn't know what a bean is, so it can't take them apart - unlike records with record patterns (and future constructs that will build on it).
Re Lombok: No, those data classes don't have record patterns. Also, not every project wants to depend on a tool that uses internal JDK APIs and is thus a potential migration hazard.
I mostly agree with this take. I realy fail to see value provided in this example. The salaried/freelancer employee made sense only in a sense that if you choose to model your business requirement as Java classes, by hardcoding requirements to subtypes, than yes pattern matching is kinda nice. With compiler type checks, etc. But I still fail to see reason for modeling code in that way..This example wouldn't save a line of code compared to procedural else-if in Java 1.4:
if ("salaried".equals(employee.type) paySalary(employee);
else if ("freelancer".equals(employee.type) payInvoice(employee);
People are gonna scream type saftey, but te moment sombody adds "unknown" employee to db table without consulting you, you are dead in water. Been there, done that. The only real type safety is DB column type.
I guess compiler dudes really love their vistors. But end of the chain app developers in real world never use it. There is no polymorphic subtyping problem on the third floor of your accounting firm.
In general sealed classed and records are really strange turn for Java. Such an arbitary syntax, and ad hoc concepts never to be seen in any other language.
This 'when' keyword when if would perfectly nice...
Currently (< java 21 LTS) Lombok is better in every way. Immutable structs w/o some mechanism like lombok's @With are so cumbersome it's actually a step backwards in every respect to DX. Thats even ignoring all the stuff like @Builder
However, record is part of the JDK so will be used in its apis like matching and destructuring. record will eventually become a value type, at which point it will have a performance reason to use.
@@bariole If you want to count lines, you should probably include the ones it takes to add and populate a `type` field in every employee implementation. 😉 (And not all data comes from databases.) But this is not about line count, it's about compiler support. If I add a new subtype to a sealed interface, I don't need to jump through hoops to find all the if-else-if chains I need to update - I just follow the compile errors.
@@nipafx In my experience, I've encountered issues every time I've attempted to model user data into Java types. Code with hardcoded types often breaks when a third party submits a value to your app that doesn't map to one of the predefined types or enums. The only solution in such cases is to deploy a "new version."
Example of: excessive type enforcement -> nice compiler checks -> production failure.
I understand that this video is a presentation of a new language feature. Arguing about the semantics of the example is kinda useless. Nice video though.
⚠ Pattern matching does not dispences to follow Liskov subtype substitution principle
Liskov subtype substitution principle is something about what people like to talk, but mostly do not care about. Subtyping is mostly done for code sharing.
@@bariole I consider SOLID principles to be part of fundamentals of OO programming. Like a baker who puts yeast in his bread
@@rahff99 This isn't OO; that's exactly the point. Turns out you don't need OO the vast majority of the time and the complexity isn't worth it.
The content covered is very good. Unfortunately a lot of extra background noises which is very distracting. Also it would be great of we could show more code than the presenter himself. I was very interested in seeing what he is typing than how he is typing. Thanks.
Nice Job Legend.. But Sir would ask cant java also go into web assembly or a Single Page Application.. and not Angular React.js and Vue
Bout time they got pattern matching, even gdot script has it lol
More FP, more merry
More FP? What next ? Monads, typeclasses, applicaties and functors ? Just rename Java to Scala
@@oleksiyprosyanko7772 You would be wrong if you think I have a problem with that 😏
Why do I have to declare anything at all? Feels like a `case Salaried -> processPayGrade(grade)` should be enough. I would think it's clear that `grade` belongs to the implicit `Salaried` object/record that's in scope for that case statement. Shadowing an existing `grade` variable feels reasonable.
What am I missing?
What you describe is flow typing and it works reasonably well for type patterns. But there's no uniform path from there to more complex patterns as, for example, record patterns, for which you need to introduce new variables anyway. So Java went with what it calls flow scoping, which requires new variables (that are in scope everywhere the check is true - hence the name).
All that said, there's also a compatibility problem. Imagine an overloaded method `accept(Object)` and `accept(String)`. Today `if (obj instanceof String) { accept(obj); }` calls the first variant but after flow typing is introduced, it's suddenly the latter. That's not good.
@@nipafx i think i'd need to see an example. It doesn't seem so different than just using something like "with" in kotlin; all it does is not make you have to explicitly qualify the member. in practice collisions and the like don't really happen.
this feels like the typical "late to the party with a compromised offering" java is infamous for.
@@adambickford8720 I really think flow scoping is more powerful than flow typing and that will become apparent as we get more involved patterns: Now they will all follow the same structure (need to declare a new variable) whereas otherwise we had one special case (every pattern introduces new variables except this one pattern) - I prefer the more uniform approach.
That said, it's not really an option anyway because of the incompatibility I pointed out. You have the example right there: It shows how introducing flow typing will change which methods get executed *in existing code*. And what you call "collisions", I call "overloading" and that definitely happens a lot in my code. 😃
@@nipafxHow can there be existing code for a feature that doesn't exist?
I'll assume its like records and, someday, they'll have a purpose but are half baked as-is.
There is no code for a feature that doesn't exist - that's the problem! There's just code - and then the new feature comes along and changes what some of it means (I gave you the example: Your proposal would silently change which method overload gets used). Java doesn't do that.
And records aren't half-baked, they're exactly what they need to be: transparent carriers for immutable data. And we can already see the transparency paying off with record patterns. (Some people think records exist to avoid boilerplate and that's when you should use them, but they're wrong and using records with that motivation will be very unfulfilling.)
In fact, you can guarantee compilation errors for enums if you use "return switch(type) {". In this case if you add a new type - you will get an error
That's true and another reason to organize the program's flow via return values instead of void methods that change some state. But the problem is that you simply don't always have something meaningful to return.
What's recommended to do if you need to permit around 40 implementations ? Go back to OOP ? Something else ?
I think switch still holds here. Otherways may be to implement interface to group similar processing classes and use enum on those interfaces.
I guess in such a case you may be able to create sealed class hierarchy (i.e. sealed sub-groups and further use sealed types over those sub-groups). Well, it certainly depends on the use case.
Java's new class-file API (JEP draft #8280389) has even more types than that and is entirely data-oriented. As previous commenters said, in such situations you'll very likely have a deeper hierarchy, so most types end up with no more than half a dozen inheriting types.
If all the concretions are known at compile time, yes. It's essentially the same exact thing, just explicit at compile time vs loading at runtime.
So the Open-Closed Principle is not a thing anymore?
Before I try to answer that question, keep in mind that, as the video explains, the presented approach is not meant to *replace* OOP but *complement* it. In no way am I trying to say that you should throw OOP out and do this everywhere - instead use pattern matching or data-oriented programming (DOP), where it fits the problem. So OCP will still be a thing in those portions of your code that follow an OOP style.
So let's ask "Is OCP a thing in pattern matching/DOP?" And that's not easy to answer. A sealed hierarchy of records is not open to extension by inheritance or adding fields, but it is very open to adding operations. Is that enough to fulfill OCP? I don't know. 😃
Thanks for the nice video!
My pleasure, thanks for watching and leaving a nice comment. 😊
Why can’t Java deduce implementation of a class / interfaces based on class’es metadata on compilation time instead of relying sealed classes?
In terms of sealed classes with switch exhaustiveness, I think it should also work for non sealed classes too since all implementations of a class / interface can be resolved on compile time.
Is there anything I am missing in this regard? I think this can be another useful enhancement
It's actually somewhat uncommon for all implementations of an interface to be compiled at the same time. You have separate compilation every time you extend or implement an interface that doesn't live in the same JAR, e.g. from the JDK, from one of your third-party dependencies, or even from your own code that lives in another subproject.
Beyond that, sealed classes also express an *intent*. It's not just that all implementations are known right now. You intend to always know all implementations.
HI @@nipafx,
I completely understand the intent part, but even for 3rd part JARs, should the compiler need to compile it and throw a compilation error if there is a switch case with a missing class type of an interface?
I am thinking about a more generalized solution for the exhaustiveness of switch statements in terms of interface/class extensions. At the very least, all JAR needs to be compiled at the very end - prior to running the code - and there might be a solution (I bet it is not easy with multiple JARs from all different libraries) to catch all missing cases just before running the app.
I just wonder, does the *sealed* introduced have a proper intent, or is it a current solution for the exhaustiveness?
@@Ambusher306 To start at the end: I strongly doubt that there will be other mechanisms than `sealed` to determine exhaustiveness.
Before answering the rest of your comment, I need to clarify a something. You wrote "even for 3rd part JARs, should the compiler need to compile it" and "At the very least, all JAR needs to be compiled at the very end" - this seems to imply that there is a conceptual phase between building a JAR and executing the code therein. That is not the case! Bytecode as it gets loaded from a JAR is executed as is and no additional compilation takes place, so there's no step where exhaustiveness could be checked.
You might be thinking of just-in-time compilation but that step is purely a performance optimization. It must not have any impact on the semantics of a Java program. Furthermore, it is inherently unqualified to apply the checks you're describing here because it does not compile all extensions of a class or all implementations of an interface at the same time (think about it, if it wanted to do that, it would have to compile every class at once because they all extend `Object` or, if you remove that as a special case, it would still compile a ton of classes because so many implement common interfaces like `Serializable`).
But even if there were some step after packaging a JAR that would make a list of implementations and apply that as exhaustiveness checks at run time, it still wouldn't get you anything. At compile time (i.e. when you're writing the code) that list doesn't exist and so the compiler only has two choices: force all switches over non-sealed types to be exhaustive (as it does today) or allow all of them to be non-exhaustive - either way you don't get any meaningful support when it comes to future-proof switches.
@@nipafx, thanks for the clarification. I was suspicious of what I was missing. Now it all makes sense 👏
Thx for content
very good!!
Great. Thanks
most of the things here like this enhanced switch exist in scala for ages only a lot better, i donno why not just "copy" as is. for example there is no need to define "Salary salary ->" as if u are checking the type the "employee" inside that branch is used as Salary.
sealed types - kotlin&scala have those for ages
Strings as triple quotes exist in scala/kotlin and seems better than the new java way, same with variable plugins "hello $someVar"
Procedural programming still alive :) after all oop hype?
Great content🎉
cool but all we need is get rid of setter getter functions, we want properties
and extension methods)
Quo vadis Java ?
Java pattern matching is still quite primitive compared to other languages. Hopefully there will be a lot of improvements in the future.
I find it already works great in everyday programming, but there are things that can be improved, I agree. Out of curiosity: What do you think should be added?
@@nipafx They plan on adding the ability to extract record parameters into variables, and I believe matching on the values. Once they do this, it will be “real” pattern matching like in Scala and Rust.
@@kurzweil4 You mean like at 17:23?
@@nipafx I missed that. That is partially what I mean. In more advanced pattern matching you can do:
case Salaried(“foo”, 5, 10)
case Salaried(“x”, 25, 100)
case Salaried(_, _, 1000)
I don’t think that is possible with the version demoed here. Though I would be happy it I am wrong.
@@kurzweil4 You want to filter by values right? A la "execute this branch if the `Salaried` instance has name 'foo', etc", right? Brian wrote this about them four years (!) ago: openjdk.org/projects/amber/design-notes/patterns/type-patterns-in-switch#do-we-need-constant-patterns
At 21:40 who thought that adding an underscote to pattern matching was a good idea. Seams like loosely thought out idea
What specifically do you not like? Are there any issues you foresee?
The single underscore was deprecated as a variable name in Java 8 (2014) and forbidden in Java 9 (2017) with this exact purpose in mind. And it's used with the semantics of "unused/unknown/unspecified" in languages like Haskell, Scala, Kotlin, JavaScript, and many more. So it's a well-established concept that proved useful across a wide variety of languages and use cases.
@@nipafx Personally I do not like that you have to declare it in the pattern matching, although it is not used on the right side of the statement. I would have preferred the ability to not write anything at all there. But that is a minor thing to complain about.
Good thing they picked up the pace. Otherwise this would have been released in 2030 😁 If they keep this up maybe some day it would even make sense to use plain Java instead of Kotlin. The only critical problem still present is the null handling. Optional is not an option. Pun intended.
As a past java dev and now a kotlin dev I have one question - why do people still use java if kotlin is like 10 years ahead and allows seamlessly program for jvm?
Java has caught up to Kotlin in most cases, and in some cases gone past like virtual threads. Personally I find Kotlin too bloated with too many ways to do the same thing and too many unnecessary key words. It's slowly follows the path of Javascript, C# and C++ with too much clutter in the language. The Kotlin syntax is also more noisy than Java in my personal opinion and harder to read. For the most part I find server applications written in modern Java (17+ with preview features) easier to maintain than the Kotlin applications in our organization.
That beeing said, the programming language is probably the least important thing when it comes to good software design. The skill of the programmer is what matter most. Deep knowledge in application architecture, deep domain knowledge and strong communication skills matters a lot more.
Is it though? Where are it's virtual threads? Where's the deconstruction? Where are multiline strings with indentation detection? Where is string interpolation that includes domain-specific rules? I'm sure I can come up with more... Kotlin may have started 10 years ahead but that's no longer the case and chances are it will evolve similarly to Scala (whose developers have asked similar questions to you up to a few years ago).
Because your premisse is wrong. Please explain to me how is Kotlin better language than Java 8 with Lomobk? Kotlin is mostly syntax sugar atop of Java. At same time it is proprietary technology without support of any of big enterprise shops.
java 21 with lombok is 90% of the way there and certainly not worth the tooling, training, etc challenges. We're a far cry from kotlin vs vanilla java ~8. And it feels like kotlin is slowing down, not speeding up.
cuz kotlin is weird dude
I like the idea of the video but please drop the music, its very distracting and I had to skip the Brian Goetz segment altogether where its very bad.
The guy clearly knows a lot. But all these neons, background music, camera changes, memorised "speech"... people can't just open an ide and code naturally anymore? Feels like we are in a TV show.
It amazes me how even Java gets really better it still sucks 😆 Looks like it will never reach the ease of use and safety of more recent languages. Sorry Java.
Java 21 brings pattern matching … well . Scala has had it from the begging. Nevertheless . Pattern matching is a functional programming feature which is an anti pattern for classic OOP paradigm . In OOP we use polymorphism instead. Java just ugly and extra verbose. Such elementary feature as pattern matching introduced as a breakthrough while in Scala or Haskell it is just a usual syntax construct
Modern Java works great with functional programming. These days I do way more functional programming than anything else in Java. Functional Java code actually looks pretty similar to Scala in many cases. I find functional programming pluss domain driven design pluss Java's easy to read syntax a match made in heaven in terms of maintainability.
It's a breakthrough *for Java* 😉and it's just usual syntax for it now, too. And if used as explained it *is* polymorphism, so "in OOP we use polymorphism instead" doesn't really make sense. You probably mean dynamic dispatch via inheritance hierarchies, but the video explains in detail why that's not always a good choice.
I'm convinced scala died because of its community, not its tech.
Yeah but Scala is dead so who cares? And Haskell is not a practical language. No company uses it. It's a fun language to play around with to understand functional programming but there is no job market for Haskell developers.