What's new in Java 12, 13 and 14
java.christmas
java.christmas
Also, the bit about LTS is problematic. While the multiple LTS update paths have their place (although the widely in their offerings and intended audiences), they are not less risky than the default path. The default path is designed to be the cheapest and easiest, but requires a little bit more agility. The effort required to update to a new feature release is not much bigger than the effort required to update to a patch release, especially as some of the LTS programs have major new features in their "patches." An LTS program should be chosen only once it's been established that the default, recommended update path is not right for your organization, and even then you'll need to compare the different LTS programs, as they differ from one another in about as much as LTS differs from the regular update path. LTS should not be the default choice.
I think people have taken note of the success of Kotlin, and decided that judiciously chosen language features will help user retention/adoption.
Language changes don't aren't just bells and whistles. Lambdas changed how nearly every API works and record types have the opportunity to make a huge difference. Sure var was smaller, but it's been refreshing.
I also think you're underselling how conservative the old minor patches used to be. With major versions this frequent LTS atleast sets a reasonable cadence for planning and movement. 6 months is hectic.
Up until JDK 9, there used to be patch releases every couple of months, and big feature releases (which used to be called "limited updates") every six months. In addition to bug and security fixes, those included large, potentially disruptive features — like JavaFX and support for Linux on Arm in 7u6, Java Flight Recorder and Mission Control in 7u40, AppCDS in 8u40, and even support for Mac OS and a new GC algorithm in 7u4. Those releases also stopped getting patches after six months, when the new one was released, i.e. there were no more patches for 8u20 once 8u40 came out. Now, despite having major, disruptive features, those "limited update" releases were not allowed to change the Java SE spec, so they couldn't change the language or the library API. There were serious problems with the old model, so Oracle decided to do away with major releases altogether. To do that, they relaxed the limitation on spec changes in the six-monthly feature releases, and also changed their name. Instead of 8u40, 8u60 etc., they now get a new integer number. If you don't want to use new API and language features (you can even turn them off with the --release flag), those releases are almost the same as they used to be. If their six-month cadence between 8u40 and 8u60 was good enough for you back then, there's no reason it shouldn't be now.
(I work on OpenJDK at Oracle, but speak only for myself)
(Did a decade+ at Oracle, and now at a shop three times its size. You should understand inflexibility and moving at the speed of business. :P )
There still are quarterly patches to the LTS releases (7u241, 8u231, 11.0.5), with the next batch due the second week of January for all the LTS versions - 7/8/11 on the Oracle side, 8/11 on the Adopt OpenJDK, and other OpenJDK implementations possibly supporting all three. Almost every other organization I've talked with is sticking to the LTS, with plans to jump from JDK 11 to JDK 17 when it comes around. (and hearing the woes of all the folks still trying to get past JDK 8)
I agree, which is why we've done away with major releases altogether. There have been feature releases every six months with major new features for over ten years now. All that's changed is that we've changed how they're named and removed the major releases. And if you've found it acceptable that the 8u20 feature releases stopped getting patches after six months, and upgraded to 8u40, it shouldn't matter much that we now give those versions an integer number. Not only do you not need to package a new major release every six months, but you don't need to package a new major release ever again!
Your assumption that it's significantly harder to switch from 11.0.2 to 12 than it is to switch from 11.0.2 to 11.0.3 does not seem to be based on any data we've seen. Most organizations that can do the latter can do the former. Not only the result will be cheaper and easier overall, but you'd get to enjoy all the performance and serviceability features even if you don't want to use new language/library features.
> Almost every other organization I've talked with is sticking to the LTS, with plans to jump from JDK 11 to JDK 17 when it comes around.
That will change soon (it already is changing), as more people understand the new model and the change to the version names. However, even if you are certain that your choice of LTS is justified (and I must say that it doesn't seem like you fully understand the new model yet -- which is OK, many don't, and it will take time), you must test your code on the current JDK. Updating from 11 to 17 without doing that is taking a huge risk. You will see features disappear without deprecation warnings.
If upgrading from one stable release to the next stable release removes features without a deprecation period, something's wrong with the release model.
LTS is a service designed for organizations with special constraints, and should be no means be your default choice. Moreover, there are multiple LTS services and they differ quite widely in what they offer and who they're aimed at. Put simply, LTS does not mean what it means in other projects. Do not choose it until you've learned exactly what the new model means and what LTS means.
I think this the italicized portion is the disconnect between what Oracle wants Java users to do, and what they'd rather not do. I know the pain of wanting your users to always be on the latest and greatest version, but in my experience, medium-to-large corporates are extremely risk averse and would rather invest time upfront to do testing/qualifying software releases before deploying, then keep that deployment running for as long as possible with the assurance that the configuration is stable (or at least has known & documented failure modes). They understand the new model just fine - they just don't want it. Mostly because it introduces a lot of risk and/or requires continuous qualification, which is not only expensive but completely changes the way they operate.
Moreover, there are also special risks to LTS programs, particularly the free ones. A much smaller number of people work on them (about 1/50 of the total number of people working on OpenJDK), and they completely rely on backports from the mainline. So, for example, once the CMS collector is removed from the mainline, it becomes unsupported in some LTS programs (certainly the free ones) because there are no longer fixes to backport, and they don't have the resources to fix bugs without backports.
Finally, you can't talk about risk and treat LTS as a single thing, as the different LTS programs vary greatly in their risk vs. conservativeness. The Oracle LTS service contains almost only true patches: bug and security fixes, like the first two patches for each feature release. Red Hat's LTS/OpenJDK Updates and Azul's Zulu backport huge new features in their "patches." Amazon considers (or considered) taking the current VM and backporting it as a whole to an old JDK version in a "patch."
I agree with you - and I don't think LTS have less inherent risk. The distinguishing feature of LTS releases is the length of support - you can still release minor versions weekly, but still bless every 25th minor release as the LTS versions and it would work out the same because the users will know that version will get support for X years without full requalification.
You could very well be right about the 3rd-party patches/back-porting (I'll take your word for it, I'm now at the periphery of the JDK world), but their existence could be a sign of how Java users are desperately unprepared to support evergreen releases.
The very notion of a "support period" becomes less relevant because what is the version that is being supported? It used to be a major release but that is now gone. The old six-monthly feature releases were also "supported" by patches for only six months. There were no more patches for 8u20 once 8u40 came out, and people were fine with that and not desperate at all. So now that we name a version 12 instead of 9u60 it's not fine? If you like, you can think about it as if we're on Java 9 forever, with an eternal support period. It's just that how the versions are named has changed.
People still don't understand the meaning of the new versions and the change to the version naming scheme. With time, they will. In the meantime, some of them still apply old terminology to new concepts and reach wrong conclusions.
Overall, I wouldn't underestimate the importance of syntax features. After all, if a developer likes writing your language, they'll more likely choose it over a different language.
The problem with language changes is that to make use of them, your developer community has to educate themselves on the new features and how to use them. Certain VM changes like new GC algorithms often don't require this, as the basic interface of the GC is the same (clean up garbage for me, thanks!).
I think the next big programming style Java may enable is what I call data-oriented programming, which is enabled via sealed types and pattern matching. This enables you to code the dual of OO, where you have a fixed number of sub-types but an unbounded number of operations. I believe this style of programming is useful far more often than it is used, simply because Java doesn't make it easy to code in this style.
Other future changes I am excited for, but don't expect to get implemented any time soon, if ever, frankly, are:
* Improved native code interop. I consider this to be both a language and VM change. * Improved memory layout control, including stack-allocated types and inlined objects inside of other objects. * project loom for golang-style I/O programming. * full tail recursion. Why was this feature rejected from the VM?
Most if not all of them will land in the next few years. In fact, working on those precise features is what most of the OpenJDK team does.
> Improved native code interop. I consider this to be both a language and VM change.
That would be Project Panama, making its initial, partial delivery in JDK 14 (GA next March, EA available now).
> Improved memory layout control, including stack-allocated types and inlined objects inside of other objects.
Project Valhalla. The most complicated of the bunch. Just had a recent major breakthrough, and you can work with the EA release already.
> project loom for golang-style I/O programming.
Working on it :)
> full tail recursion. Why was this feature rejected from the VM?
Not at all rejected. It's still a goal for Loom. We'll just do lightweight concurrency first. Cost/benefit prioritization etc.
How do you think it should do this? While keeping the semantics of Java.
What does structural typing refer to in this context?
The language changes are all just trivial tweaks to javac. Appreciated niceties, sure, but nothing meaty. The VM changes are then likewise focused mostly on the GC. Again, appreciated, but it's entirely disconnected from the language.
Where's the long-overdue improvements to the bytecode? Where's value types? Where's runtime generics?
Reified runtime generics for reference types is a bad idea; it destroys language interop for very little gain. Value types and specialized generics for value types, on the other hand, are in the works. You can download and play with them here: http://jdk.java.net/valhalla/
It is a common feature in languages derives from the ML family[0].
[0]: https://en.wikipedia.org/wiki/ML_(programming_language)
Needless distinctions like that make the fresh air a little less fresh...
There are plenty of almost entirely pointless features that get implemented in major languages, like 'events' in C#. They add almost no value to the programmer. Pattern-matching would be really useful for avoiding rats' nests of control-flow, but until recently no major imperative language even seemed to consider adding them.
The extremely obscure Felix programming language has had pattern matching for years. [0] As nestorD says, Rust has them now, as does Kotlin. About time.
I was pleasantly surprised when I discovered Python 2 would pattern match tuples in function parameters...and then very disappointed when I discovered a few weeks later that Python 3 had removed even that minimal amount of syntactic sugar.
- with objects you can easily add new types, but it's difficult to add functions (methods) dealing with these types
- with sum types you can easily add new functions, but it's hard to add new variants in the sum type
Is it possible to overcome these limitations? I first read about the expression problem in the excellent post "The Expression Problem and its solutions"[1] on Eli Bendersky's blog (discussed here[2] on HN); as the title suggests it does present interesting ways to "solve" the expression problem.
However he also points out that the chosen solution in a a typical programming language (visitor pattern) quickly becomes unwieldy. The second solution (multimethods) is much nicer to deal with, but requires support from the language.
[0]: https://en.wikipedia.org/wiki/Expression_problem
[1]: https://eli.thegreenplace.net/2016/the-expression-problem-an...
It seems like these treatments tend not to get to the heart of the expression problem as seen in actual language tools, which is migration between language versions. Let's say you're on version 5 of a language and you want to migrate all your tools to support version 6 where there is a new expression type. Can your codebase clearly represent a situation where some tools have been migrated to support version 6 and others aren't done yet? Can you easily figure out what remains to be done? And once the migration is done, can we remove any traces of the previous version that we don't want anymore?
And how do we approach this if the AST is published as a library and each tool is a package written by a different team?
There might be other usages of sum types that are simpler, though.
Not only that it makes every possible event explicit and stand-out on its own, which is good for inline optimization and documentation (think about the catastrophic event handling in JS world), it also provides a standard, much more intuitive syntax using formal function delegate declaration (think type-safe function pointers), vastly different than what we do in JVM.
Before having lambdas in Java, we need to add an EventListener as a variable and adding an extra interface, so event handlers are insidious to write, that you have to write a new class, implement the specific interface, write some shim properties to store externally-living variables explicitly, and finally "new" that class as an instance, and add it to a specific event listener, which is not only verbose, and also costly, in terms of memory use (it has to be backed by vtables rather than simple functions) and time taken to implement it.
Well after the long-awaited introduction, Java finally have limited lambda support that just generates a class and it have some odd issues with, for example, enforced effectively final variable reference [0], but in C#, you have delegates and events almost from day 1 -- and it handles all that event mess nice and clean where nobody can still beat that simplicity and elegancy even till today.
[0]: https://stackoverflow.com/questions/34865383/variable-used-i...
I agree that's nice to have - essentially announcing events as special in the type system.
> good for inline optimization
Any optimisation here should be possible with an ordinary implementation of the observer pattern, no?
> it also provides a standard, much more intuitive syntax using formal function delegate declaration
I'm not convinced that it does. Without events, we can still write:
var h = () => { doStuff(); doOtherStuff(); };
subject.registerObserver(h);
> Before having lambdas in Java, we need to add an EventListener as a variable and adding an extra interfaceI suspect we're both right, then: events were introduced for a good reason, but now that C# has lambdas and such, they don't seem to add much.
And I find that we disagree. When I frame a question as, "How does this algorithm handle this value?" they frame it as, "How does this class behave in this algorithm?" Is the difference in how the types are treated in the algorithm best expressed as the concern of the class (via polymorphism) or as the concern of the algorithm (via pattern-matching)?
I write a lot of OO code with polymorphic methods and am not opposed to modeling things that way, but I think it's often not the best way. I feel like business logic that could be expressed coherently in a single place gets scattered across many classes, and to understand the algorithm you have to gather the logic from a bunch of different places and reconstruct it. Not only that, classes accumulate little fragments of logic that belong to disparate concerns that are supposed to be handled elsewhere. If you have polymorphism and not pattern matching, this is inevitable. If you have both, it can be avoided.
A big gap between Java and C# is value types and better interop with native code ("pointers"). ByteBuffers in Java are painful for many usages and very poor compared to things like [StructLayout(LayoutKind.Sequential)] in C#.
In contrast, Java hasn't changed all that much over the years, and I wonder if an approach of taking the more useful features from C# and ignoring some of the others would be a good approach.
I often wonder how Java developers feel when they look over at C#, and see a language that has exploded in functionality over the last decade, all while Java has mainly optimised the JVM and slowly added features.
BTW, Java has also "exploded in functionality over the last decade", it just hasn't translated to language changes. Even the project I work on, adding lightweight concurrency, will not change the language at all, while in C# it took the form of a huge language change (async/await). Nevertheless, in Java the added functionality will be at least the same as it's been in .NET.
I actually wonder if it's more taking features that have proven sexy, and ignoring the rest. I still think that the single biggest source of verbosity - and design damage in some of the newer APIs such as streams - is that Java hasn't implemented extension methods. That costs me time and money on a regular basis. By comparison, the cost of fall through by default in switch statements is that I have to use a linter. Which I already have to do for a fistful of other reasons, anyway, so, while this -> operator certainly scratches an itch I've had, it doesn't move the needle much in terms of productivity or code quality.
edit: Should add, in C#'s defense - .NET's lightweight concurrency was initially implemented as a library. C#'s async/await came later, and is just syntactic sugar as far as I've ever been able to tell.
Absolutely true. When I've looked at that research, I was really quite impressed by how little has gone into looking into it, considering how interesting the subject is to so many people. My guess would be that it's because it's prohibitively expensive to study. That said, there was one result that I believe was shown to be fairly robust, and independent of language: that bug rate and cost are both generally proportional to lines of code written.
To that extent that that may be true, while I certainly don't have a $500,000 study by a team of professors at Stanford to back me, there's at least a plausible basis for my own perception of doing better work in object-oriented languages that do or do not have some sort of mixin mechanism: I find that using them often lets me get the same job done in less code. (Without that, I admit I have to retreat to pointing out that absence of evidence is not evidence of absence.)
There's also the design damage thing. Some developers on my team are quite resistant to using streams instead of loops in Java, and it's precisely because of the poor ergonomics. If you need to do an operation that isn't built into the Java API, you have some sub-par options: You can implement a collector, which is justifiably criticized as being a hassle (6 methods to implement) that yields code that scans poorly (every other verb is "collect"). Or you can implement a function, but using that function requires breaking the flow of the stream code by creating a bunch of intermediate variables, or, worse, constructing a pyramid of doom. By contrast, when we're working in Kotlin, you can just write a function and deploy it the same way you'd deploy any other method in its equivalent APIs. It's less effort, it's less code (read: stuff to get wrong), and, perhaps critically, it's a lot less annoying.
Bad language design and semantics definitely costs money in the long term. For example, Tony Hoare refers to Null References as his "billion dollar" mistake. The verbosity of Java before sophisticated IDEs would also have cost significant time and money. Java is improving significantly (and arguably was never as bad as say JavaScript or PHP), but there are some legacy decisions that will be very hard to change; and so improvement can only ever be incremental.
You seem to hint that some hypothetical non-small improvement can be made differently (in some other language). Perhaps it could, but it doesn't seem anyone has done it yet. We do not observe large differences between and in companies based on language choice. I think some of the reason is that developers overestimate the cost of coding in the entire software development process.
Hmm, I think it's pretty clear that most folks are far more productive in Python than say C++. But yes, I agree that there's no silver bullet, programming is hard.
I am a long time Java programmer that has mostly programmed C# lately.
I'm getting more conservative by the years but I still found myself liking modern C#.
The thing I miss from Java is the ecosystem:
- three top notch IDEs
- more mature ecosystem
- maven
Optionals and the new IO libraries are causing far more churn in my Java code and the libs I use than any recent C# changes.
The Java language doesn't feel conservative to me. It just feels slow. What was worth the wait? What does Java do better than C#? The only thing I miss while in C# is Java's take on enums.
All that said, I don't begrudge Java as a whole. The JVM and other priorities add a lot of value and I'm glad their adherence to bytecode compatibility remains strong. The language is the trade off but that doesn't mean I need to pretend its better.
there appear to be accounts dedicated to posting from javascript.christmas, java.christmas, and functional.christmas , all of the same style/format/etc.
the only posts they submit are from those domains. is this a coordinated boosting effort?
functional.christmas => https://news.ycombinator.com/submitted?id=bendiksolheim
javascript.christmas => https://news.ycombinator.com/submitted?id=ewendel
Seems like they are trying to attract developers, shame their main site is Norwegian.
Is there a rule against authors posting their own content? It's not some grand conspiracy, it's just an christmas campaign by a company that has asked their employees to write about topics that interest them, and those articles are shared during advent. On the 25th it'll presumably go quiet again until December 1st 2020.
Java 8 had its end-of-life for commercial usage in january 2019. The new long term release is Java 11.
Time to move on for you.
Naturally the gap between (say) Java 10 and 11 is fairly small, meaning it shouldn't take many months to merely discuss an upgrade - but not all companies have got round to the regular-release way of thinking.
But I see your point about the new release schedule, where the LTS version is not supported after 6 months.
I think the companies need to change their mindsets. New Java version are backwards compatible, as they introduce changes gradually.
It is actually more dangerous to wait, because they risk that some features (like GC) are deprecated after 4-5 versions. By updating regularly and keeping an eye on deprecated features, they should have time to adjust
A good thing that came out of it was that it forced them to untangle the 2 decades old standard library. This was disruptive but it seems to have also unblocked a bit of progress and also allows the to have experimental modules in non lts releases (9,10,12,13).
So on reflection I think it was a good move. Most frameworks and libraries work on modules now.
It's only a drawback for hired hands that may have become used to the features in v13 and then land a gig where they have to scale back to v1.6, that has to hurt.
_Oracle's_ Java 8 end-of-lifed, but many other vendors provide TLS for their respective Java implementations.
For actively developed projects, I would recommend moving to java 11 without too much delay unless you have pressing technical or business reasons not to. If it's dead code and it is not causing issues, don't mess with it too much.
The Java 8 to Java 11 upgrade is unfortunately somewhat disruptive due to the module stuff. That affects some projects that depend on JVM internals, which tends to include e.g. older versions of application servers. Upgrading those is a bigger deal usually.
They still used to gate bigger language changes on major version releases, so upgrading point releases to major versions at a 6 month cadence let's changes roll out more as they're done.
Iiuc they're treating every third version as a candidate for LTS versions depending on your vendor. So people who for features are excited for every release, but those care for a little extra stability care about 8, 11, 14 etc.
EDIT: It went Java 1.4 -> 5.0[0]
No it is not.
The new Java shorthand, by introducing a new variable name, also introduces some sneaky variable shadowing risks (as this blog post itself explains!)
Edit to add: maybe the difficulty is in formally specifying “smart casts”, or at least clearly documenting them? I don’t think Jetbrains has documented all the rules used by Kotlin. It doesn’t seem undoable, though.
(Google hasn't cached it yet as of this writing)
enum Result<T, E> { Ok(T), Err(E), }
I have no idea why the Kotlin std lib has a Result type but limits it to exceptions - such a missed opportunity.
We used to joke about APL (the "beautiful diamond") and Lisp (the "ball of mud"). Lisp was big, at the time, but most of it is what we'd call the standard library today. The actual core was quite small, and everything was remarkably coherent for a system of its size. Today, many core languages are as big as all of Common Lisp.
When I see a language add new syntax for a trivial transformation, in the compiler because the language isn't extensible, using ASCII art because they've run out of symbols on the keyboard, it just looks like the worst of APL combined with the worst of Lisp.
Meanwhile Java's REPL is a pretty sad imitation, and I see no movement toward trying to get back in the user app space.
I love the JVM and the language itself is good enough, but the management of Java features through the years leaves me disappointed.
* I know Android is Java and Kotlin focused but Android development is entirely different than writing a JavaSE app. Also know Kotlin compiles to JS, and I think that supports my grumpy young man persona. A hugely successful language IDE company saw it useful to build a cross compiling language on your platform.
If I can stay in Python and target the browser without the fuss and bother of JavaScript, why would I?
There are also more technical reasons like WASM not being able to access DOM.
If it’s the simple kind like python in bash, it’s really primitive compared to an IDE where you can inspect things. Why doesn’t a simple project suffice?
For my part, a REPL helps me think about what I'm doing. There's a design process prior to writing code--sketch things out on paper, do a mind map, assemble pieces, get a general architecture. In the thick of coding, though, it's nice to be able to try out an idea or two in real time, test as you go, and feel out how the code is taking shape.
REPLs are really an invaluable tool for thinking about code in real time. For some people that may not be helpful or necessary, but it's expedient for me.
Python and JS were designed for small experiments. I would wager 90% of Python code per capita are single file scripts.
Java is for building whole systems.
The reason Java is losing traction compared to the Python/JS type languages is that the type of programs people write are different. In the 90s, the majority of programming was on big infrastructural things like word processors, control software, web browsers, etc. Now that programming has gotten so much more popular, more people (by plurality) are working on comparatively smaller, more toyish things. It happens that Python/JS is the better tool for this kind of work.
If software is to keep growing at the same pace, the new programmers will be absorbed by the Python/JS camp. Python/JS will keep rising. There is a maximum number of kernel developers or compiler developers in the world, but the amount of small toy apps that can be created is infinite.
Trying new features is a good way to broaden your skill set, and if there is something you strongly dislike about the usability of a feature you can even provide feedback to the JDK developers.
To me it reads like: Try our experimental features because that will make you a better (rounded | paid) developer, and if you really really want you may even provide feedback.
I read that as: trying new stuff in code generally makes you a better programmer. And when you test stuff that's still in an experimental phase, the language designers are probably still open to feedback from the broader public.