Hacker News new | past | comments | ask | show | jobs | submit login
New language features since Java 8 to 17 (advancedweb.hu)
345 points by dmitryminkovsky 3 months ago | hide | past | favorite | 351 comments



Unfortunately it looks like Java is going to evolve enough to take the wind out of the sails of Kotlin and Scala for most dev shops. I guess the positive take is that those improvements might not have happened without the efforts to develop better JVM languages.


This is great not unfortunate. If Java improves because of Scala and Kotlin then so much the better, this is how languages evolve. If it really does evolve to remove the need for a better Java then it would be nice if people switched away from Kotlin and back to it. Scala can then carry on cementing its niche as a powerful commercial FP language. No hate for Kotlin at all but it seems like in that world, there might be no need for it. Scala is better for FP and if Java 17 is the better Java then great.


To be honest I see Kotlin continuing to exist because of it's multi-platform nature and it's focus being on different things to Java.

So even if feature wise the 2 equalize (a good thing not a bad thing IMO) stylistic differences will remain. Many of Kotlins syntactic differences are unlikely to make it to Java for instance.

I'm ok with this. I use both for different things. Kotlin for most app level development, Kotlin for libraries that are only useful in the context of Kotlin, Java for everything else so it has minimal dependencies and can be used from Kotlin/Scala/Java/Clojure equally well.


Hmm yes I guess you might be right given Android. It's a shame in a way that Kotlin was invented as Scala could have filled the niche too imho but that ship has sailed. It's too similar to both Java and Scala and doesn't really bring anything new to the table. Is that fair or does it have a great USP other than massive Google backing?


I don't think Scala ever could have filled that niche, I say this as someone that wanted Scala to be successful.

The truth of it is that Kotlin is incredibly easy to learn, even for non-Java devs. It is definitely focused on a very low barrier to entry. I don't think this was an original goal but it's become a significant focus of the language especially after being selected to be the next platform language for Android.

This isn't true of Scala at all. Beyond things like SBT, comparatively poor IDE support (less of an issue these days) it is also just a very complex language, with many complicated semantics in it's standard library and even more complex/fancy 3rd party libraries piled on top of that.

Scala became an incredibly powerful language, not a simple or easy to use one however.

Kotlin is a very user friendly language that is easy to learn but still has enough power to keep most expressiveness focussed devs happy.

Beyond the learning curve Kotlin integrates with Java a lot better, calling in both directions.

These 2 factors alone probably account for why it was chosen over Scala for Android and in general why it has value over both Java and Scala.

I think with Gradle moving from Groovy -> Kotlin, Android obviously and big companies adopting Kotlin w/Spring on the backend the ship has indeed sailed for Scala, it doesn't really have a place outside of places that specifically want a more FP lang.


I hear this a lot about Scala, and I am a Scala dev so maybe I'm biased, but I don't get the difficulty angle particularly. I'm in no way exceptional as a programmer or IQ-wise but I picked up Scala easily as part of my first job. The difficulty thing doesn't compute to me but it could just be that I'm well-suited to the language. It works beautifully as a better Java if that's how you wish to use it. Granted, it might be hard if you start with a "pure FP" codebase but most devs don't struggle picking things up on most code bases even as juniors.

The point about tooling and developer experience I'll concede. It has been at times patchy in Scala in the past but it's pretty good these days.

As I say, I have no hate for Kotlin, and I'll possibly use it one day being a JVM developer familiar with Java as well, I just find it unfortunate that a language which is so similar to the languages it's influenced by was invented.


The first time I tried Scala, I got a type error based on the result of an implicit conversion, which succeeded locally but not for the entire expression. It took me a while to figure out what had happened. Advanced (mis) features such as this combined with encoding everything into objects with subtyping make Scala very complex. F# and OCaml are examples of much simpler languages that have most of the advantages (albeit no JVM).


Implicits were majorly revamped in Scala 3, so do give it a look if you hadn’t already!


It's difficult because the language is gigantic compared to Java, it has all the OOP bits Java has (and Scala needs in order to be relevant) then a whole bunch of FP stuff on top. This is a lot for newer programmers and while they might be able to do "hello world" equally quickly I do feel like they soon hit a wall of "I don't understand why this works" when looking at idiomatic Scala.

It can be used as a better Java but requires discipline. Things like implicits can easily cause an unnecessary amount of pain. Kotlin on the other hand implemented extension methods in a way that doesn't detract from the better Java paradigm. This is only one example though, there are many many reasons why Kotlin is more suited for the "better Java" role. Namely it's simply not that different to Java. It's effectively Java with a more modern syntax and polished extensions to the Java standard library.


Scala 3 somewhat simplies implicits by the way. Especially w.r.t to extension methods, which are now a first class construct in the language: https://docs.scala-lang.org/scala3/reference/contextual/exte...


It does require discipline, or at least one person on the team who can set a style guide for the newcomers to follow, this is true.


Every worthwhile language brings important choices that require discipline. If there’s only one way to do anything, your experts’ code will be just as bad as your beginners’.


Kotlin is easy to write because every idiom is available - but calling that a low barrier to entry reminds me of how articles used to suggest Perl as a beginner language because it was expressive. I actually consider it more complex than Scala as a language, because a lot of complex Scala idioms are just library code that you can click through to, whereas Kotlin tends to implement them as part of the language. I fear for the people who will have to maintain large enterprise Kotlin codebases in 5-10 years' time.

SBT is inexcusably awful (and so is ScalaTest, which is a lot of people's first exposure to the language), and you might be right about the zeitgeist having moved on, but it's a shame; it's honestly a significantly better language design.


>The truth of it is that Kotlin is incredibly easy to learn

That's not my truth.

I tried it, after not doing "real" programming for a while, and found it the most obfuscated, incomprehensible language I've ever run into. What new concepts are there in Kotlin that justify it?

"Expressiveness" sounds toxic to me - along the lines of "facilitates a personal idiom that makes your code impossible to understand and you irreplaceable".

After several days trying to do the simplest form of asynchronous processing in an "hello world" Android app, I switched back to Java, and everything made sense again.

I also wrote a small program in C# and that too was just as easy as I recall from ~10 years ago.


Yeah I'm not going to argue that Java and C# aren't smaller languages than Kotlin, they definitely are and I like/appreciate both.

I'm surprised you feel Kotlin is incomphrensible though, it definitely can be written that way because it has features that allow for extreme terseness.

I think there is a minimal set of things you need to learn over Java for Kotlin to make sense, nullability operators, block passing syntax and the differences in OOP (inheritance/interfaces are slightly different, classes are closed by default, companion objects replace static methods).

So yeah. Kotlin is strictly larger than Java so it takes longer to learn all of it.

That said because of the look and feel of Kotlin it's more accessible to developers of Ruby/Python/JS/Typescript. This tradeoff is personally worth it for me.

Not because I care that much, I can just as easily use Java. However it's -very- difficult in modern times to convince my team and company to use Java when there is hipster options like Typescript taking all the air out of the room. Kotlin competes in the same area but provides the same battle tested runtime and excelent libraries, it's almost like a compromise between the new age and the old age.

This is why it's such a great language for me in particular but I realize not everyone is in the same boat.


Kotlin can be written very densely indeed. It's a bit like Scala in the regard that disciplined usage is necessary (although nowhere near as bad, no implicit conversions/parameters, restrained operator overloading).

But then, I've seen some shit Java too.


Which big companies are adopting Kotlin on the backend?


You could soon be able to use modern Java on Android, too. Remember that most of the new syntax features don't need changes to the JVM.


Android will always be Kotlin-first. That's a pretty huge market.


I think people underestimate how deep this integration is going. Compose relies on extensions to the Kotlin compiler. The future of Android and Kotlin are pretty much tied together at this point.


Android used to be always Java first. It could change again.


Most likely to Flutter. When Google will be ready to ditch Android API.


Android is based on the JVM, it will never switch to Flutter (and an aggravating factor is that Dart is an inferior version of Kotlin).


The JVM is specifically the part of Java Android didn't use, and the layer Google has already completely replaced once.


DEX must be able to represent everything on the Java ecosystem, otherwise it is a hit and miss what can be consumed from Maven central.

Also it is quite common on embedded Java for the vendors to have their own implementation with code translation.

What isn't normal is having stagnant compatibility.


Java is an Android's curse. Just compare how completely native iOS and iPadOS beat it in terms of speed and power saving. I hope they will get rid of the Java remnants completely and focus on the native toolchain, frameworks, system libraries.


Meanwhile there's a article literally on the front page about zero-click exploits being used against iMessage


Java doesn't help against exploits, there are plenty in Android phones as well.


It certainly adds a layer of protection that makes issues like the classic C errors with buffer overruns and derefing invalid pointers a LOT less likely.


Android doesn’t run java though? It runs native code the same way as ios, so bringing the latter ecosystem’s better performance into the picture without considering the generational leap in terms of CPU design is just a faulty logical conclusion.


If you want some fun watching Jake Wartorn's opinion on it,

https://youtu.be/VX6nAvRWQg4


Scala is fine. People understood it's not really worth it as a "better Java". Companies that choose Scala in 2021 are using either big data frameworks (where Python bindings are the most common alternative to Scala, not Java), or functional ecosystems (Typelevel, Zio). Scala 3 has taken an interesting direction and to me it seems that Odersky wants to win Python developers over rather than Java shops using Spring.

New features in Java and the JVM (records, loom, valhalla, ...) are directly going to benefit Scala and make it even better.

On the contrary, Kotlin's essence has always been threatened by the fact it's nothing more than a better Java. I assume there's still a big demand for that on Android, but if you're running on a recent JVM, I would certainly consider Java over Kotlin.


Kotlin started out as a better Java but it's more than that now. With Kotlin multiplatform you can compile to JVM, Javascript and Native from the same repository.

Java has been catching up on language features but I'm not sure if I'm ready to switch back to Java for Spring projects yet. Mostly because of nullability, extension methods and coroutines in Kotlin.


Targeting multiple platforms is orthogonal to the core language design goal, which has always been to be more like C# on the JVM, with 100% Java interop and no need for a big standard library.

I give you nullability, however extension methods without proper typeclasses is just syntactic sugar, and coroutines will be superseded by Loom.

Java hasn't only been catching up, it now supports pattern matching which Kotlin still lacks.


Targeting all those platforms will result in Kotlin falling in-between all of them. It will not be truly close the the JVM primitives, or the native ones or both in the end and will suffer from platform-specific quirks.

Especially with the heavy, under-the-hood developments of the JVM, like Loom and Valhalla.


Which is a bummer for folks that loves the cleanliness and power of "basic Scala" (pure functions, immutability) without wanting to get into deep FP theory (typelevel, zio). That has been a really nice pocket to be in for teams that have the discipline to not get clever with it. Same with teams wanting to integrate with Akka on scala.


I think between Scala 3 and the Li Hayoi ecosystem, there is a path to simplicity. But that usually means you lose part of the appeal of the language, since you can't use some parts of the ecosystem.


Zio is pretty much what you want. No need to understand category theory, no over-reliance on implicit resolution, ...

That said you don't need to understand that much to use high-level libraries in the Typelevel ecosystem. And when I do need to reach for low-level constructs, I find nice that everything is built upon clean and composable abstractions.

You mention Akka, it's kind of its own beast. The inner workings go definitely way beyond "better Java", yet Lightbend needs to provide a surface API that plays nice with Java interop. It's not an ecosystem easy to navigate in my opinion.


Interestingly, I have the exact opposite opinion.

Scala has been declining steadily for a decade now, and Scala 3 is going to precipitate this state of affairs by splitting the shrinking community even further.

Kotlin, on the other hand, has done nothing but grow for the past five years, no doubt helped by Google's formidable support for it. I just took a quick look at one of its Slack channels, "getting-started", and it has over 38,000 people on it. And that's just one channel among dozens more.

I don't think Java's latest additions are anywhere near threatening Kotlin's rise to dominance since Java is moving at a very slow pace, by design, while Kotlin supports some features and design directions that will never happen in Java.


It's not in decline imho, it has settled as middle ranking language in terms of popularity and is v popular in its niche. It depends where you are of course but in London it's used in loads of places from major corporations to startups. The market is hot.


I used to contract in London, I still occasionally get info about contracts from recruiters, Kotlin largely replaced Scala, compared to 5 years ago.


Kotlin is growing for sure on the server side. Far from replacing it though.


Dominant outside Android? Keep dreaming, where is the KVM implementation by JetBrains?

Yeah right they had to reboot it.


Kotlin is in frequent use on the backend in many places, including Amazon and Google.


Just like any JVM guest language since Beanshell, and all lumped together are a tiny dot on JVM deployment world wide.

Google is Kotlin's godfather, naturally they are using it, just like Amazon with their own Android flavour.


I don't think this is going to happen with Brian Goetz as language architect. He refuses to add many features that would address everyday pain points such as:

* null safe navigation operator

* properties

* mutable records

* a way to ignore checked exceptions (or at least having the stream API take functional interfaces that can throw exceptions)

* adding functional methods like .filter()/.map() directly to collections instead of having to .stream().map(..).collect(toList())


He does not refuse, but needs to look at bigger picture.

Some features are low hanging fruit, some are proposed small changes that actually should be defined a bit differently to be better and they will take more effort.

But even small hanging fruits means that they need to chose which ones to do. Should they delay pattern matching by 6 months to get elvis operator? I wouldn't like it, pattern matching is more important.

Basically they have limited resources and have to chose what to work on.


Thank god for that, because those feature are either horrible on their own or superseded by better ones. You're getting a car and complaining for not getting faster horses. The features Java has adopted -- specifically, algebraic data types and pattern-matching -- will lead to better development practices, rather than making it easier to work with inferior ones.


> Thank god for that, because those feature are either horrible on their own or superseded by better ones.

Are they though? I agree mutable data classes and property _setters_ are bad, but what about the rest?

I personally like the idea of checked exceptions, but there is no doubt they failed to deliver on Java. Perhaps the good parts of this idea can be salvaged by having an easy way to explicitly wrap a checked exception, like with_context() in the anyhow Rust crate. Kotlin dumped checked exceptions in favor of Results, and I don't see how Java has anything right now that supersedes that.

Explicit nullability is unarguably a good feature. You could make a convincing argument that expressing it with an Option/Maybe is better than the road that Kotlin, Swift, C# and TypeScript took. But Java's Optional<T> is not that solution, since the T within the Optional can still be null. And I'm not even getting into how verbose and unnatural using it feels, or the small issue of having to rewrite all the existing Java APIs to make it work.

The same goes for having eager collection operators. The JIT compiler might be able to inline and optimize simple stream operators, but the ergonomics of doing simple things on a collection is just bad.

But even if Java does all of that, just lack of extension methods and top-level functions make using Java a nightmare for me. If Java had extensions methods (or an equivalent concept like Type classes or the Uniform Function Call Syntax that D has), the previous complaint about lacking collection operators directly defined on Collection<T> and Iterable<T> will be a non-issue. Just let anybody who wants it define their own.


> I personally like the idea of checked exceptions, but there is no doubt they failed to deliver on Java.

Why is there no doubt? I agree that their interaction with lambdas leaves much to be desired, but those problems can be fixed.

> Explicit nullability is unarguably a good feature.

It is. It wasn't on the list, which asked for a "null safe navigation operator"; that is not a good feature, as it makes it even harder to see where the nulls are.

> But Java's Optional<T> is not that solution

I didn't say it was. Nullability types for Java are being studied.

> But even if Java does all of that, just lack of extension methods and top-level functions make using Java a nightmare for me.

Extension methods make a language a nightmare for me, though. Java doesn't try to appeal to all people, and no language ever will. It just tries to appeal to most. As for top-level functions -- stay tuned.


  those feature are either horrible
I speculate that most users of languages that have these features (Kotlin has all of them) would be unhappy if you took them away.

  algebraic data types and pattern-matching -- will lead to better development practices, rather than making it easier to work with inferior ones.
I am not arguing against those features, but the ones I asked for seem easier to implement by comparison as they are already in other successful JVM languages. Furthermore, they are far more frequently useful for my use case of web services storing, transforming and moving data around, often without caring too much about their semantics. I speculate that a large portion -probably a majority- of JEE/Spring devs are in the same position.

I am disturbed by how universally true you seem to consider your positions - as if there were no cases in which mutability is preferable to immutability (even if that is sometimes just due to the way some ORM or serialization library works).

If these features are such bad ideas there should be plenty of stories of Kotlin/C# devs cursing the language for providing them with these footguns.

Having a JDK dev respond like this only strengthens my argument that cageface has no reason to fear that Kotlin will lose steam - Java has different priorities. There is nothing wrong with that, but more developers who want these features should be aware that they will not be getting them from future Java versions.


Adding syntactic sugar for properties to the language will cement the worse than ideal get/set naming convention to the language forever, while there are sure better abstractions that could be made instead (eg. records with withers)

Mutable records, especially primitives are a pain in the ass for C#, so it is a bullet avoided. Immutable objects are much easier to optimize away, and are safer to use in concurrent code.


> I speculate that most users of languages that have these features (Kotlin has all of them) would be unhappy if you took them away.

Kotlin has very little impact over the ecosystem due to its small market share (compared to Java). Plus, it has no influence over the platform, or any of the other platforms it targets for that matter, with the possible exception of Android, although I guess that's up to Google. The best it can do on the Java platform is make current practices a little easier. Java, on the other hand, has the option of changing the platform and the ecosystem and moving it toward better practices.

> in other successful JVM languages

Just so that we're clear, for over a decade now, the portion of Java platform developers using all other "successful JVM languages" combined has been pretty much steady at 10%. That's not to say they're not popular in absolute terms, but Java is so stupendously popular, that it's in a completely different league.

> If these features are such bad ideas there should be plenty of stories of Kotlin/C# devs cursing the language for providing them with these footguns.

Those languages appeal to different people, and, in the case of Kotlin, at least, they're the best they can do given their clout. If I don't like something, there will be those who do. The question is, how many? When I said that the features are bad or superseded by better ones, I didn't mean they're necessarily bad for all people or all languages, just that they're either bad for Java or that Java can do better.

> has no reason to fear that Kotlin will lose steam

The way this works is that the alternative languages on the Java platform fight for market share with each other. Kotlin will not disappear because of Java, but it will eventually lose its market to some other alternative language, just as Scala has lost to Kotlin. 10% of people just prefer other languages, and I am very happy that the platform can accommodate them. We'll continue working to make sure that the platform is a good target for different languages that appeal to different people. Other languages don't take away from the Java language, but make the platform more appealing to people who prefer less mainstream languages, and so we all win. Java's 9/1 ratio of Java language vs. others is a pretty good one, and we're trying to preserve it (we would be even happier with 85/15 or 8/2, as it probably means a larger market share for the platform overall rather than a smaller market share for the language. Remember that OpenJDK is funded by people buying support; they're not buying support for the language, but for the platform). If the non-Java-language market has shifted from Groovy and Scala to Kotlin, as that's what most of those in that group prefer -- so be it (although I do wish more people use Clojure; it is, IMO, the most interesting and best design alternative Java platform language out there, and one of the most interesting and best designed languages around in general).

The strategy that keeps the VM is innovative and the language is conservative has worked very well for Java, and because it's expected to remain very popular for a couple of more decades at least, we are very careful about adding features. The goal is to avoid new features as much as possible, and the way to do that is to add fewer, but more powerful features.


I can understand the hate behind null-safe operators, but why properties? Why is bad to have an ability to "upgrade" a member variable into a property, without breaking any existing contracts? I really hate that I need to resort to using setters and getters in Java because those are bad for searchability.


Properties are probably the worst feature on that list, and no experienced language designer would add them to Java. They make it easier to work with setters, while the goal is to reduce their usage altogether! Records achieve that much better. Unencapsulated component access is standardised on classes with good contracts (construction and deconstruction are duals for records) and will work well with patterns, all while avoiding unnecessary mutation for such classes (instead, we'll have "withers" or "reconstructors": https://github.com/openjdk/amber-docs/blob/master/eg-drafts/...).

So we're killing two birds with one stone: making it easier to work with "simple data" while also reducing reliance on getters and setters. On the other hand, C# finds itself needing to add more and more capabilities to this feature, while Java dodged that bullet.


> instead, we'll have "withers" or "reconstructors

When?

> On the other hand, C# finds itself needing to add more and more capabilities to this feature, while Java dodged that bullet.

Java "dodged it" by never even having to fight. While C#'s "poor choices" are leading to a language with less and less boilerplate with almost each year, in Java we'll have to wait another 10 years before it delivers half of a half of the "withers" functionality that doesn't work with half of existing standard library code (the way arrays, lists and collections are three non-intersecting entities).


But at the other hand, C# is heading in the direction of C++, where even after n years working with it, it can still surprise you/find yet another way of doing this and the rest.

Like, I would prefer not having 10+ initializers from C++ in a language.


But on the other hand you have object initializers directly in the language

In Java you have one of these:

- a hope that someone provided a useful static `.of` method

- a hope that someone provided the 15000 setter methods that you have to tediously invoke one by one

- a hope that someone provided a generator that created a builder that has 15000 setter methods that you have to tediously invoke one by one

Meanwhile in C#:

   new Object {
     x = ...,
     y = ...,
     z = ...
   }

for literally any object (provided properties are public of course).

And don't get me started on array vs list vs collections none of which are compatible with each other. And hundreds of other QoL improvements that are begging to be implemented, and are not.


Easier construction for records will come to Java. As with most Java features, it will come once it's been carefully considered, and done in a way that gives the most bang for the buck (e.g. we do want easier object construction, but if we add a feature for that, we'll try to get more than just easier object construction out of it).

> And don't get me started on array vs list vs collections none of which are compatible with each other.

This is not true. Lists are collections, and while arrays are different, Arrays.asList() gives you a list view of arrays.

> And hundreds of other QoL improvements that are begging to be implemented, and are not.

First of all, just in the past five years we got local variable type inference (var), switch expressions, records (with much more to come), sealed classes, and basic pattern matching (with much more to come), and string templates are around the corner. All of those are significant QoL improvements.

Second, every feature comes at the expense of all others -- both because of opportunity cost and overall language complexity budget -- and the question is one of priorities. Not everyone has the same priorities.

Finally, because Java is expected to remain extremely popular for at least a couple more decades, we must be very careful about adding features. While other languages are trying desperately to gain many developers right away, we're looking ahead to ensure that we'll be attractive to developers in ten and twenty years from now, and so must spend the complexity budget carefully.


> Easier construction for records will come to Java.

Once again: when?

And why only records? Why does everything in java require someone to manually create an .of method, or create 15000 setter methods or generate those 15000 setter methods?

> This is not true. Lists are collections, and while arrays are different,

I remember running into an issue with the difference, but can't remember now. Quite possible it was an artefact of an early implementation.

> Arrays.asList() gives you a list view of arrays.

Yup. One of the hundreds papercuts that litter Java.

C#:

    var list = new List<string>()
    {
        "carrot",
        "fox",
        "explorer"
    };

    var array = new string[]{ "carrot", "fox", "explorer" };

    ... use whatever IEnumerable methods on both ...
Java:

Ahahaha. No. For lists use a static .of method and be glad that someone manually wrote that in the library. And be thankful that there's some shortcut to create an array. But for Lists? eff off, no shortcuts for you.

Oh you want convenience methods on arrays? Ahahaha, no. Use a separate helper to convert into a list first. (This is so bad that IDEA suggests some version .toList as the first method on I swear 90% of array/collection/list/stream operations).

Because, as someone said, "our goal isn't to adopt the strategy of less successful products, but to forge our own".

I'm really surprised records made it into the language with this approach to things.

Edit: Note: Ii use Java and C# interchangeably at my day job. Man is C# a nicer language with literally hundreds of QoL improvements that make you scream "why didn't Java adopt these?" I still reach for Java as I'm more comfortable with it and it has an unparalleled breadth of libraries.

But every time I need to write or implement and write another X.builder().setX.setY.setZ.build() instead of `new X { x=.., y=.., z=.. }` I want to claw my eyes out.


> Once again: when?

When it's ready. Again, because of Java's position, we aim for features that will work well for decades rather than those that will grab some more users now. I know this is hard for some to understand, but the main complaint we're getting is that Java is getting too many features too quickly, not the other way around. The people who like lots of features are a minority; a large one, but still a minority. Whether we add a feature or don't add it, some people will be disappointed. It's impossible to please everyone, so we try to please the majority.

> Ahahaha.

    var list = List.of(
        "carrot",
        "fox",
        "explorer");

    var array = new String[]{"carrot", "fox", "explorer"};
This is not where we think the language's complexity budget is best spent. When we do add a feature to the language, we want it to have much more bang than that. Those who like lots of features don't like that but, again, they're the minority. I cannot stress enough how much people hate language features. Language features don't have Javadoc, and people have to learn them. If they have to go and learn a new feature, they expect it to be worth it.

> I'm really surprised records made it into the language with this approach to things.

You mean the approach that has consistently made Java so successful? We try to add as few features to the language as we can afford to, which means that we make the features we do add extra powerful. This gives us room to add some of the features people will be asking for in ten years, rather than waste all of the complexity budget now.

> "why didn't Java adopt these?"

Because Java's strategy is different, and it's working better.

> But every time I need to write or implement and write another X.builder().setX.setY.setZ.build() instead of `new X { x=.., y=.., z=.. }` I want to claw my eyes out.

With that I completely agree, which is why we'll address that.


> When it's ready.

So, I'm likely to be dead by then.

> You mean the approach that has consistently made Java so successful?

Why are you adding all these features from all these "non-successful languages" then?

Why is it that dealing with long chains of setter methods isn't "spending complexity budget" and is a "feature more bang than that" (whatever "that" is), but adding literally the same thing: better ways of working with collections is "no, we don't adopt features from less successful languages".


> Why are you adding all these features from all these "non-successful languages" then?

The issue isn't the features themselves but the evolution strategy. Sure, we've been adopting most of the features added to Java since 1995 from ML (1972-3) and we're nearly done, but what we don't adopt is the approach of adding lots of features that each help a little. We like few features that help a lot.

> Why is it that dealing with long chains of setter methods isn't "spending complexity budget" and is a "feature more bang than that" (whatever "that" is), but adding literally the same thing: better ways of working with collections is "no, we don't adopt features from less successful languages".

We don't adopt an evolution strategy -- not features -- from less successful languages just because they're doing it. Java's strategy has been to innovate quickly on the VM (and we're innovating there more than anyone else, I think), and keep the language relatively conservative and relatively slow-moving. And it seems that more people like it this way. For the others, we make sure that the platform supports a choice of more feature-rich languages with good interop.

But the answer to your question is that we're trying to add as few features as we can (to leave room for growth in the futures, as requirements and fashions change again) and we believe that the features we do add help more than the ones we don't. Still, at the end of the day, there's hardly ever consensus among developers on anything. Whatever we do or don't do, some people won't like it. What's important to remember is that even if in some cases you don't agree with the language team's decisions, they are among the most experienced and successful language design teams in the history of software (I can say that because I'm not on the language team). They might make mistakes from time to time, but overall, they know what they're doing. In any event, the argument that millions of people prefer Pepsi to Coke is not the one that will convince Coke to change its flavour to that of Pepsi, because even more people prefer Coke.


I know it's probably to late to add any value to the discussion, but you can write this in Java;

    var list = List.of("carrot", "fox", "explorer")
You are right about the builder, but most projects I have worked with use Lombok and the @Builder annotation to generate the builder automatically. I agree that a native syntax for this would be better.


It was more of an example on C++, and I agree that many features of C# are great QoL improvements! But at the same time, I do feel that sometimes the language committee would be better not implement a feature just yet and wait for a better abstraction that makes 2-3 features possible at the same time. That way the cognitive load of the language would decrease by a lot.

And yeah, C# feels to me to be on the way to being way too feature-packed, which can be a detriment in the extreme.


Thanks for the thorough explanation. Now I understand where Java is heading for. The aesthetics regarding withers concerns me a bit though, because I don't like prepending anything to the name of an acceessor (a transformer, in this case?), which was why I liked the idea of properties in the first place.

I hope both Java and Kotlin can converge into a common coding style. As you said regarding C#, Kotlin's emphasis on properties makes it easier to use getters and setters and thus may hinder getting rid of them in the Java ecosystem. I'm not sure if I should support Kotlin's success at this point.


> will lead to better development practices, rather than making it easier to work with inferior ones.

C# has these "inferior practices" and is a much better language overall. Why Java doesn't adopt the low and not-so-low hanging fruit from C# is really baffling.

Anything from yes, properties, to object initialization shortcuts and a sane IEnumerable/Collection interface that doesn't require stream/toList everywhere.


> C# has these "inferior practices" and is a much better language overall. Why Java doesn't adopt the low and not-so-low hanging fruit from C# is really baffling.

Because Java is doing a lot better than C#, and obviously we feel Java is a much better language overall.


Java has had quite a head start (.net core came out only 5 years ago).

Now the "doing a lot better" is dwindling. And dragging feet on QoL isn't helping.


The gap between Java and C# has grown very slowly but surely for many years, and more people feel Java is changing too quickly than too slowly. Different people prefer different things, some will always prefer Pepsi (although C# will probably not be that Pepsi, as it's lost too much ground and MS seems to be losing interest in it, as they tend to do), and our goal isn't to adopt the strategy of less successful products, but to forge our own, even if some on HN have other ideas.


> it's lost too much ground and MS seems to be losing interest in it

wat

> our goal isn't to adopt the strategy of less successful products

This sounds like "we don't care if some other languages have great quality of life improvements, we pretend that success (for some unknown definition of success) is the only thing that matters, so we'll keep implementing one or two features every 10 years that don't work with half the language and standard lib, and still require hundreds of lines of boilerplate to work with."

In a sibling comment you're pretending that withers is a thing that will ever happen. It won't, not for another ten years. Meanwhile we're left with a language that has to resort things like Lombok to try and cut down on the ridiculous amounts of code one still has to write for even the simplest things. Or almost but not quite incompatible lists and collections (none of which have even a pretence of a DX for easy construction). Or value types that have been a proposal for 9 years, and generics with primitive types that has been a proposal for 7. And the list (or collection? or stream? or?...) just goes on and on and on.

Thank god we finally got multiline text blocks and some form of pattern matching. Wait. Those come from less successful languages. How could you?


> wat

Their focus seems to have shifted to TypeScript, and that's understandable as they have far better chances for success there. Their best hope for .NET was to make it the Pepsi to Java's Coke, but that hasn't materialised (in fact, it's getting further away). .NET has not been anywhere near the go-to alternative choice for people who just don't want Java for one reason or another in years.

> This sounds like "we don't care if some other languages have great quality of life improvements

I'm sorry it sounds that way to you. It means, "we understand that some portion of developers prefer more features, but we cater to the majority, and, given that Java has a very long future, we'd rather pace ourselves." For the quick-feature-loving minority -- which we also care about -- we make sure the platform can host more feature-rich languages that interop well.

> It won't, not for another ten years.

You're very wrong about that. Remember that only five years ago, Java didn't have var, didn't have switch expressions, didn't have text blocks, didn't have records, didn't have sealed classes, and didn't have pattern matching. More people complain that Java is getting new features too quickly than too slowly.

> Wait. Those come from less successful languages. How could you?

It's not that we don't adopt features from less successful language; it's that we don't copy their evolution strategy. Our goal isn't just to make sure Java is very popular now, but also that it's very popular ten and even twenty years from now. This means that we have to budget language complexity very carefully. We also have the new developers learning Java in 2035 to think of.


> Their focus seems to have shifted to TypeScript

C# 9 has more features in the release (one year after the release of C# 8) than java had in the past five years.

Whatever "seems" to you, it clearly isn't true.

> > It won't, not for another ten years.

> You're very wrong about that. Remember that only five years ago, Java didn't have var, didn't have switch expressions, didn't have text blocks, didn't have records, didn't have sealed classes, and didn't have pattern matching.

Me: Java is moving too slowly

You: no-no. Some people even think we're moving too fast. Here, we have five features in five years. Some of those features should've been there 20 years ago.

> it's that we don't copy their evolution strategy.

It's a great delusion. Java's development is abysmally slow. It has nothing to do with "quick-feature-loving minority" as you so condescendingly put.

Java moves in fits and starts, taking 5-10 years to add some features that often don't fit with either the rest of the language, or with half of the standard library. Almost literally everything takes near a magnitude more boilerplate to write and develop than it should. And when it's not, it's only available to some parts of the lang, and not to the other.

But when you express concern about this? Oh, then you're a "quick-feature-loving minority", and not a developer earning their living with Java and hoping for any meaningful improvement to it.

Oh, I'm sorry. I'm not smart enough to understand why a possible solution to tolstoyesque method chaining calls that will maybe come to Java in 10 years is "evolution of the language", but literally the same solution to collections (and unifying lists and arrays) is "a quick feature from less successful languages, and we don't adopt evolution strategies from those".

As I don't understand a hundred other things that can elevate Java-the-language from mediocre to great.


> C# 9 has more features in the release (one year after the release of C# 8) than java had in the past five years.

Not even close. They're adding small features to the language, while Java adds huge features to the runtime. .NET is 10-15 years behind Java on the runtime. Roughly speaking, Java's philosophy (going back to Gosling) has been to innovate on the runtime and keep the language conservative, while .NET's has been to add a lot of features to the language and keep the runtime relatively crude. There is no indication that changing our strategy would be a wise move.

> Some people even think we're moving too fast. Here, we have five features in five years.

Not some; most. Most people prefer fewer than five features in five years, even though many prefer more. I really wish we could make both you and the others happy, but I don't see how we can do that other than what we're already doing: have Java be the popular mainstream language, and support other languages on the platform for those who prefer something else.

> Some of those features should've been there 20 years ago.

Should have been there so that what? Or at the expense of what? Our goal is to build a successful language used by millions, not to push the envelope in programming language design (although we are pushing the envelope in runtime design). We introduce features when we think mainstream programmers could use them in the best way, and we add them slowly enough so that we're not overtaken by fashion and we have room to grow for decades. You may not like it, it's okay, and it's even perfectly reasonable. But this has worked really, really well.

> It's a great delusion.

Fine. Maybe you have more access to market data than we do. Fact of the matter is, no other language is doing any better, and Java has fewer serious competitors today than it did in the last 15 years.

> But when you express concern about this?

I am not dismissing your concerns. I am merely explaining that we have about ten million other developers we cater to aside from you, and they don't all share your opinions. Your views are far from unique, and we are well aware that many people want the language to evolve faster -- and that's perfectly legitimate although I personally don't think it's a good idea for such a popular language -- but even more don't.


> we think mainstream programmers could use them in the best way, and we add them slowly enough so that we're not overtaken by fashion and we have room to grow for decades

Ah yes. Using a single common way to initalize things and unifying arrays and lists (so that you don't need helper modules, and you don't have to rely on the kindness of libs to provide .of methods) is "fashion" (as are many other things).

But "yeah, we will maybe some day 10-20 years from now provide a way to build records, and only records, in a way that doesn't require hundreds of lines of boilerplate", that's long-term strategic planning and evolution.

Well, we're talking past each other. I'm well aware that any change in a language the scale of Java is a daunting task. But the condescending tone that dismisses something as "features for the quick-feature-mided fashion crowd" clearly shows that it's not these concerns that drive these decisions, but purely subjective outlook on things.


I am not dismissing you just pointing out that while your preferences are reasonable, legitimate, and are shared by many, they are also far from universal. I'm a developer, not a diplomat or a marketing person, and if different people ask for opposite things, I don't know how I can say yes to both.

I think that being honest about our strategy that dates back to Java's inception -- an innovative runtime and a conservative, slow-changing language -- that has worked well for us and so we're sticking to is more respectful than giving you some non-committal marketing response. I think you should also be respectful and understand that the Java language team is one of the most successful and experienced language design teams in the world and know what they're doing, even when people might disagree with some decisions. Even experts disagree, and people have different tastes (I find C# and contemporary Kotlin positively garish, while I think Clojure and Zig are elegant and beautiful).

And the language features Java does intend to adopt will come quickly, just as the smallish-medium ones over the last few years have. I believe lambdas were the only language feature that took more than a couple of years to deliver. Once the language team decide they want a feature, they deliver quite quickly. The features that take a long time are usually changes to the runtime (what I mostly work on), not just the language.


Are you saying that e.g. pattern matching is a "small feature"?

As far as runtimes... CLR bytecode is expressive enough to compile C++ into it. Try that with JVM.


Question from a non Java programmer, where can I read more about algebraic data types in Java? The article doesn't mention the term.


They are not used that much yet, and the whole deal (with destructuring pattern matching) is not yet ready, but it is basically just records as (nominal) product types and sealed classes as sum types.

What is already possible with switch expressions is branching based on type, in a much more ML-like style like case Point p -> … which doesn’t require instanceof checks and castings, but it will soon become Point(x, y), where the x, y will automatically get the values of the corresponding fields.


Stream has been given a `toList()` convenience method:

https://docs.oracle.com/en/java/javase/17/docs/api/java.base...()


Is that true though? nullability is a area I've heard /u/pron mention may be tackled in the future, mutable records seem like they will be tackled with the 'withers' concept in the future. Checked exceptions + changing the sugar of .stream().map I don't see ever changing. Properties I am unsure of.


Why should it be in the future. The syntax of .stream() has been awfully verbose since Java 8: in the middle of “.stream()….collect(toList())”, you need to squint to find the actual function name being applied between the flood of polluting calls, which loses the appeal of functional programming.

In my company we have f(list).map(…), which is a wrapper for the streams. But newcomers don’t like it because it’s not the standard.


AFAIK, the fate of nullability is tied pretty directly to Valhalla. It's constantly being brought up there.


IMHO safe navigation only makes sense when the compiler is able to reason about nullability, meaning nullability is rooted in the type system. That's the case for Kotlin and not for Java.

Otherwise, you've got a high risk of developers inserting a ? to fix a bug, which only fixes the symptoms.

As of now, Optional#map seems to be the way to go.


Re: Properties: What is the gain? What is the "pain point"? More typing? Can you share a language where you feel properties is a significant gain? (C#?)

Re: Streams API that allows checked exceptions. Brian Goetz has written about this issue. (Search StackOverflow.com) They did not allow because of the optionally parallel (threaded) nature of streams. If multiple parallel streams throw an exception, when/when/where/how are the exceptions re-thrown? Moving exceptions across thread boundaries is a tricky thing in any language. If you don't care about parallel streams, there are many open source projects that effective create a nearly identical Streams API but allow throws Exception everywhere. Are these open source solutions insufficient for your needs?

Re: adding functional methods like... Is there an advantage other than typing nine less characters?


> Moving exceptions across thread boundaries is a tricky thing in any language.

Not really. C++ has std::exception_ptr for that exact reason, and C# has ExceptionDispatchInfo. Both are very straightforward - you catch the exception, get a handle for it, and then re-throw that handle in another context, with original stack trace preserved.

Multiple concurrent exceptions is also a solved problem. In C#, if you do something like Task.WhenAll(), and one or more task throws, you get back an AggregateException, which can be inspected to see which task threw what. The same can be applied to async streams.


I think one could argue exactly the same thing for the same reasons about C# vs F# on .NET, which is my area of expertise - but honestly I'm not so sure it's true. C# has grown into an enormous language as it tries (consciously or not) to subsume F#, and F# is always going to remain so much cleaner just because you aren't having to ignore 90% of the language features when you code in it.


Agree with this. What's more interesting is on the feature level languages do borrow/converge on each other in order to try to secure a position in the market. I note that as an example Java is consdering a .NET like Span feature (https://openjdk.java.net/jeps/412) as an example probably to close the gap w.r.t performance. There are others.

What matters more in all honesty, and what's hard to change IMO is:

- The base of the language (is it verbose, how the base features interact with each other) vs being "tacked on". A language with these features initially. e.g. you mention F# and I know it uses HM type inference, immutability by null, makes null hard to express by default, etc.

- Anything that breaks backwards compatibility (e.g. Java's generic implementation) for example including existing API's. Adding Async afterwards takes time, and changing the underlying model of the language (e.g. Python's GIL, OcAML multithreading) takes time and needs a lot more consideration. Its code, anything can change, its just harder and maybe easier to take something that was designed for that case initially. Being a polygot isn't a bad thing per se I think if it can be justified tech wise.


Java's implementation of generics was heavily influenced by a language called Pizza. Sun hired the author of the language, Martin Odersky, to write the compiler for Java. Odersky later went on to develop Scala.

Java improving borrowing ideas from other JVM languages had happened in the past, and that has made the ecosystem stronger. Nothing unfortunate about it.


The cause and effect are quite right, though. The reason Kotlin was designed in 2009-2010 was because Java became stagnant, not by choice, but due to Sun's demise. After the acquisition and an adjustment period, Oracle gradually increased investment, and the evolution pace got back to it's "normal" course. So Java's "revival" and Kotlin's existence are, indeed, related, but one did not cause the other; rather, they both have a common cause.


Kotlin is still way ahead of Java with IMO essential features like:

- Explicit null

- Extension methods

- Getters/setters which use property syntax

- == instead of Object.equals confusion

Also Kotlin's this-scoping and delegated properties, while they take a while to get used to, can be incredibly useful. They make it possible for to write typesafe DSLs like kotlin-html.

Moreover, Kotlin is usually very easy to integrate with existing Java projects. Setting up Kotlin in Gradle is just adding a plugin, and Kotlin automatically imports and exports java code. And it runs on the JVM so it will support most platforms that also support Java. And one of the top Java IDEs and contributors (JetBrains) maintains and prioritizes Kotlin.


I don’t follow why this is unfortunate?


I assume OP prefers Kotlin or Scala.

Personally, I think one of my favorite things about Java is that, all things considered, it's a small-ish language. So I'm glad that Java is both willing to evolve (slowly, letting other people try stuff out first) but also clearly focused on a curated feature set and resistant to letting new stuff in to bloat it. I am very happy to deal with minor things like no multiline string support if it means we get to avoid being a terrible mess of language features that ruby, scala, python, etc have accrued in a relatively quicker timescale.

I was fairly sad around the java 6-7 days where we went quite a long time with no visibly major new language features (diamond inference was 7's big thing, lol). So it is nice that we're moving a bit faster than that, now.


By multiline strings are you referring to text blocks? Those have been added to the language in Java 15: https://docs.oracle.com/en/java/javase/15/text-blocks/index.... .


Oh, sorry, I know, I mentioned it specifically because it was "only" just added in Java 15 (which is basically "now" in Java timescales heh, and has only literally just now (with 17) made it into LTS AdoptOpenJDK releases, which also haven't quite landed on many distros package repositories, etc etc.). So for some weird value of "right now" Java still doesn't quite have multi line strings due to its newness.


Tbh I still crave Kotlins inline string interpolation.


A “superior” implementation is in the works with templated strings.

It will look something like this:

STR.”text with \\{variable}”, but anything implementing the TemplatedString interface can change the behavior of interpolation. There is not even a requirement to produce strings only!


Oh nice, do you have a link to the project?



Cheers!


> I don’t follow why this is unfortunate?

I'm guessing Java hate, which is kind of fashionable. If Java evolves to stay relevant, it won't be usurped and fade away.


What I don't want to see happen is innovation stop in JVM languages and then Java, no longer under threat, also stops innovating.


Java has basically no threat from Kotlin and the like. The reason it innovates now is simply Oracle’s increased pouring of money into the platform, which is especially visible now compared to the stagnation of the end of the Sun era.


A lot of people have migrated away from Java to better JVM languages. If Java actually keeps improving, there's a reasonable likelihood that people will stop migrating and those that have may come back to Java from these other langs.

Long term this could lead to a loss of maintenance for tools and libraries for these languages which could kill them and leave those that migrated with a dead & decaying ecosystem.

Of course this is a bit of a stretch but it's not unheard of.


Over the last short while I've seen Lightbend announce ceasing support for Play, others express that Slick is likely abandonware, and also seen my chosen Play authentication library, silhouette, get archived on github. All of this has impacted my favorite side project that I've been slowly plugging away on.

Silhouette is probably just because of one guy's schedule, but Play/Slick seems like after-effects of lightbend's layoffs many months ago, and while I don't know the cause of those it seems plausible it's because of the kind of thing you describe above.


A tiny drop, an endless collection of guest language that goes back to Beanshell, many of which now forgotten.


If people left Java for other language(s), wouldn't that leave Java with a decaying ecosystem?


Maybe worry about leveling the playing field first? There's so much money and research and development going into Java that there's no possible way for Java to end up with a "decaying ecosystem" inside the next twenty years. It's like pointing out DuckDuckGo and worrying about Google.


Most of them stayed somewhere on the jvm though. Still in the ecosystem.


This happens all the time with "guest" languages. A few old salts have been prophesying this right here on HN since Kotlin first came out. I think it'll still be used on Android though, it's hard to see Google pushing Android development back to Java.


Kotlin is still run by the company that makes the best Java ide, no? I don't expect kotlin to get pushed out so easily.


Scala still has a better design at its base, which is missing from Java and I don’t see how Java can retro-fit it into the language, Immutability is a big part of being functional and doing safe multi-threading stuff, java is doing all the good syntactic stuff (case class, switches with guards etc) but without immutability it’s a far cry from the same facility in Scala land. Having said that, this trend is welcome for every time I have to write Java and makes it feel more natural to me as an old time Scala developer.


With records, Java does try to embrace immutability to a certain degree, but of course it will never be completely immutable.


I don't see it as unfortunate. It shows that Java is a living language that continues to improve and change to match the needs of its users. Compare this to Common Lisp, which has not change in any meaningful way since 1984.

A lisp-er would probably say that CL is so malleable that it hasn't needed to change, but I'm not so sure about that.


Except without Java they are meaningless.


Am I the only one who's seeing "sealed" classes as an unfortunate development?

I can't count how many times I had to resolve to using PowerMock and hacking bytecode just to mock something in one of the libraries I use because it's ingenious author "final"-ed the whole API surface of the library (and bits of the implementation usually).

Now this gives even greater tool to people who love to lock everything up and think that they know better than me what I need.


Without sealed classes it's impossible to enforce most interesting invariants. If anything the other way is a mistake.


Reasonable arguments can be made that sealed classes should be the default.

From https://news.ycombinator.com/item?id=16952659 (and all the related links to that comment and the original article - The Impoliteness of Overriding Methods)


IMO sealing for classes is unnecessary, so long as methods are opt-in for overriding (e.g. as in C++ or C#, where you need to explicitly say "virtual"). If you can't override anything in a derived class, how can you break invariants?


Yeah - personally I think languages should allow a lot of constraints like sealed classes (it makes your intent clear, and can allow extra optimizations, that's good!), but highly-risky "escape hatches" need to exist because they solve real problems.

"Fork and modify" is a very capable alternative, but it's far, far, far more costly than "monkey-patch this one time issue that'll be fixed next week". And some languages make it extremely painful to achieve, often requiring significant code rewriting, and which sometimes make contributing back to the source-library significantly harder. And when such hacks hang around longer for a week... well, sometimes that's fine, sometimes it's not, and the library author is not the one who should be making that decision. It's my program, it should do what I tell it to do.


A sealed class ought to have no methods, so it is plain old data. Then there's no purpose to overriding it.


I think you're thinking of `record` classes? Sealed classes are about constraining inheriting or implementing to only approved types - using sealed is pointless without overriding of some kind. Unlike in some other languages, where sealed is closer to java's final classes, i.e. stopping inheritance.


In Kotlin, I use sealed classes as a substitute for union types, and that means for the most part they have no functions.


If they are like Scala's I do mean sealed.


The only thing I can find on Scala with "sealed" is for exhaustive pattern-matching on types, which 1) broadly matches Java, and 2) has nothing to do with data-only objects at all. Sealing and methods are entirely orthogonal.


You shouldn't need to mock a sealed class in the first place. Consider them like a more powerful enum or an algebraic data type.

You wouldn't mock en enum either (hopefully).


PowerMock is a mistake on its right own; I outright reject pull requests if anyone attempts writing unit tests that need modification of final state.


Don't think of it as a less powerful class, think of it as a more powerful enum. At least that's how it's meant to be used. It's a good feature in other languages, and like you said, "final" already exists, so you're no worse off on that front.


I've seen projects where the whole architecture/design was screwed just because someone wanted to mock >>everything<< during unit testing...


Sealed classes are better enums, akin to swifts, etc and are built to allow pattern matching and error reporting on missed cases.


Glad to see Java improve, but I still would like to see more ML features:

- ~Exhaustive pattern matching~ it’s here!

- Algebraic data types

- Tail call optimisation

- Do notation

- Operator overload

Why not use another language? Well, the name “Java” guarantees buy-in at this point. Maybe it will eventually be a Trojan horse for ML :)


The unfortunate stance on ADTs is that Java doesn't want them generalized. The standard answer is use Records and Sealed classes.

What does this mean? Well with generics we can have two libraries that know about Map<> and concrete types TypeA and TypeB and can interface type-safely using Map<TypeA, TypeB>.

What we can't do is have two libraries that know about TypeA and TypeB interface type-safely using a sealed type of TypeA and TypeB. Even without considering libraries, another use we may want to talk about a sealed type of TypeA and TypeC, but of course TypeA can only be in one of the sealed class trees.

The worst part is that Java already has a Union type that's only ever used in multi catch(ExceptionA|ExceptionB e).


I've been thinking about the lack of ad-hoc SumTypes in Java. The sealed class approximation isn't good enough. I'm seriously considering taking up TypeScript in earnest as my 'go to' language. It's an outlier in supporting A | B | C and being mainstream with pretty good runtime performance.

One thing I'd have to check is the quality of IDE support for refactoring tooling. Refactoring operations for Java using IntelliJ almost always works and as I expect or better.


For myself, I'd like to see use-site variance replaced with declaration-site variance. It's really painful sometimes to have type arguments within generics more than one level deep; you have to start adding `? extends` (usually; or `? super` occasionally) at all the intermediate levels.

I somewhat suspect Java is locked into its current approach to variance, but gosh, I much prefer Scala's approach here.


What would do notation add to Java?


Not the parent, but presumably it would allow for fancy monad stuff like Scala's Cats Effects[0] and Zio[1] which can make async programming easier to follow without having to introduce async/await

[0]: https://typelevel.org/cats-effect/ [1]: https://zio.dev


For the async use-case, “native do notation” will be superior in the form of Loom, that will transform virtual thread calls to non-blocking concurrency.

As for others, without proper Monads, I doubt they would have much use.


Not specifically for Java but I have found myself wanting do-notation in non-functional languages so that I can basically write a very expressive library while also forcing users of that library to go down my road i.e. I hacked around it in the end as I didn't have a do-like abstraction but I had (in-effect, it wasn't quite a DSL) written a DSL that represented certain constructs just fine using operator overloading, but you can't overload control flow. Therefore building the notions I wanted to in code was very painful without some kind of (equivalent notion to) do-notation.


Can you do something similar in Kotlin using this function scopes (that let you change the meaning of this in a lambda)? I’ve found those ok at defining EDSLs.


I've only ever done a hello world in Kotlin I'm afraid.


You should check it out. Definitely not as expressive as Scala for expressing EDSLs, but it has a couple of features (scoped this for lambdas and extension functions/properties that can be injected into a lambda's namespace by a scoped this type) that you can get a lot of mileage out of.


There are lots of places where it can help, but to give one example consider the Java 8 Optional type.

It’s great for avoiding nulls, but you get excessive nesting with many isPresent checks. Do notation can fix this.

The language even provides a bind function, but no reasonable way to use it!


Excessive nesting? Optional supports both .map and .flatMap.


I also recently found this site, which for example can show you an API diff between Java 8 and 17: https://javaalmanac.io/jdk/17/apidiff/8/


I just want to add that while this is a really nice post in its own right, I found this blog because its author created this: https://github.com/dodie/vim-fibo-indent


I imagine there's a reason it's necessary, but it's weird that "sealed" is basically "final" with the ability to do "permits" instead of just adding "permits" as a keyword that can be put on "final" classes


I suspect it has something to do with classfile format backwards compatibility. From a pure syntax standpoint, I would agree with you, but there's probably some value to making a very clear separation for classfile loading.


Does java have a decent story on value types now e.g can I make an ArrayList<long> and trust that it’s one array of longs on the heap and not an array of pointers to Longs? This was what made me ragequit Java 10 years ago.


Not yet. You can track Project Valhalla [0], the incubator for value/inline types in OpenJDK. But I don't get the sense it's landing particularly soon.

https://openjdk.java.net/projects/valhalla/


Really? They’ve already written the JEPs. It seems to me that they just have a few more details to iron out. Wouldn’t be surprised to see something land for Java 19.


I'd definitely be curious about which signals you're drawing on. From what I've seen, the Valhalla OpenJDK wiki has gone without update for quite some time, and even that wiki refers to JEP 169 ("Value Objects") as old.

I do get the feeling that there's activity, given that JEP 416 is targeted for Java 18 and specifically refers to Valhalla (and Loom) as benefactors of the proposal, and seeing as the mailing list is reasonably active. But since there's little official word and not much buzz on my radar (even Loom has significant buzz), I'm not expecting anything soon.


JEP 401 (candidate status) was updated this september: https://openjdk.java.net/jeps/401

Also in September, a brief status was posted: https://mail.openjdk.java.net/pipermail/valhalla-spec-observ...


Ah, very nice on both counts. It looks like instantiating generics over primitive types won't happen until a yet later JEP, and preview features tend to have a cycle time of at least two releases anyway, so I won't expect too much until Java 20. But it's still nice to see where things stand now :)


Not likely in 19 unfortunately. It is still lots of work with many many interactions with the whole of the language.


No. You can’t make an ArrayList<long> at all. You can make an ArrayList<Long> and trust the JVM to optimize as it may or you can use c++.


You can mostly trust the JVM to not optimize this the way you'd want for primitives. ValueTypes are int he works though, and using a supplementary library for primitive specialization like Trove or fastutil have become the norm for now. Not ideal, but tolerable!


If your application needs that kind of control, use a more suitable, lower level language. If you just want that level of control for aesthetic reasons, get over yourself.


Java is that language though! It has primitives and primitive arrays and the language is built around many comfortable ways of using them compared to something "a bit more high level" like a python, javascript, or ruby; but you get to keep your compacting GC, chunky reflection, runtime introspection, compared to a C, C++ or rust.

Primitives don't play well with the generics though, for decent enough reasons, so the standard library leaves out many conveniences that you can pull in as needed. Of course we're excited about them playing well together, but the sweet spot in the design space is still being worked out. We call that effort ValueTypes, lol. (well sorta. there's more to value types than just primitive specialization but i digress)

I get that teasing jabs at java are always and fashion but consider that for many, it persists because it is a bit of a sweet-sport language. I think Go succeeds when it does for precisely the same reasons.


Don’t get me wrong, I like java and hate c++. I worry about the rapid pace of change post 8. Some of the changes seem great individually but trying to be everything to everyone is how you end up with c++.


The FastUtil library is very good for this: https://fastutil.di.unimi.it


I used to really love Java, it was my second "real" programming language after C/C++, and GC, type safety, and object orientation really changed the way I thought. I also learned almost all I knew about high-performance VMs (up to 2013) in the context of JVMs.

But these days it seems like Java is engaged in one long apology for the "everything is an object" mindset it started out with. It both took it too far and not far enough. Erased generics were the start of the problem. Parametric types! You were the chosen one! You were supposed to unify the primitive types and the object types! And never adding syntax for function types, just single-method interfaces...just ugly and clunky IMHO.

C# did generics right. Well, almost. "void" isn't a real type, so you can't be generic over it.

It is really important in language design that generics can range over any type. It allows the full combinatorial space. I worked really hard to make the Virgil compiler support arbitrary combinations of tuples, arrays, function types, and all of those work together seemlessly in the polymorphic type system. Everything else is a unfortunate shortcut, IMHO.


Its not universal to all of .NET though.

F# does support this (e.g. Task<unit>) because in F# every function has only one input and one output. Therefore it needs a unit/void type at the lang level at least. This simplification surprisingly results in more power and less code compared to C#. Its part of the base design of the language, and an example of something that is hard to change afterwards in future versions.

In F# overloads for different amounts of type args to a function are not common, unlike C# with different types for each Func<T1, T2, T3, ... TResult> or Java with both Supplier and Function types and needing a custom function type for more args last time I checked. A use case that has come up for me would be to dispatch on variadic template args (e.g. loggers) in a typesafe way without reflection penalty, or looking at some of the libraries using overloads that could be reduced (especially in conjunction with inline for the value type overloads I suspect such as found in LINQ). When I see libraries with overloads for parameters like 'Func<T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,TResult>' I think of this. Wherever you see multiple overloads in C#/Java just for the sake of multiple generic arguments; that can often be simplified if required/worth it.


Parametric types and the unification of primitive and reference types is the goal of project valhala.


Whenever we read about new improvement in Java,it is always inevitably followed by concerns for viability of Kotlin or Scala. However these concerns are never applicable for Closure. I am glad I went all in on Clojure.


It's not applicable, because Clojure already lost the momentum and sinks deeper and deeper into its niche.


I don't think Clojure was ever growing at a huge clip, but it seems to be doing about the same as Scala, see eg the pie at https://snyk.io/blog/kotlin-overtakes-scala-and-clojure-to-b...

Kotlin is just Java with improvements and it has major corporate push from Google, so it's natural for it to ride the big volumes.

I think long term Clojure is going to keep doing well, despite modest pie slice of JVM dev, because lots of people continue to prefer its simplicity + dynamic FP + unique approach to data, plus the excellent ClojureScript side, and it doesn't really have competition or alternatives on the JVM (unlike Scala, where Kotlin/Java are co-opting features).


Isn't closure on its way out though, not like Perl level but going downhill? Kotlin on the other hand is raising.


Yeah I think most Java devs are a bit in awe of Clojure, just not sure about the leap needed to get there.


Anyone who values static typing will never be in awe of Clojure. Or any dynamically typed language, for that matter.

And Java developers care a lot about static typing.


> And Java developers care a lot about static typing.

With the amount of reflection and annotation-based magic I've seen in the ecosystem, I'm not sure I actually believe this? Maybe it's just lack of exposure, but most of the Java devs I've met seem very happy to undercut the type system whenever it's convenient.


Java annotations are type safe. And dynamic types is precisely what turns me off of Clojure


Java annotations aren't executable on their own, so they're trivially type safe. The systems that do things depending on those annotations are typically reflection-based, which does an end-run around the static type system and usually requires a lot of casts.

I like static typing myself, but having experienced languages with better type systems, Java's feels like a straitjacket. It's unsurprising that so much of the ecosystem (such as in Spring) leverages reflection and dynamic classloading shenanigans.

Props to Dagger for doing compile-time dependency injection well, though.


I think what op meant is that dependency injection is type safe, in that in case of going through a constructor, they have to indeed type check with the dynamically provided instances.


(I got lucky and spotted this reply instantly; I promise I wasn't lurking waiting for a reply.)

Don't dynamic DI frameworks like Spring use runtime reflection to invoke such constructors? If a bug existed in Spring causing a value of the wrong type to be provided, I expect it would go undetected until runtime, at which point Java will throw an exception.

My understanding of "type safe" is that the code is statically known not to have such defects. It's likely that we're just getting hung up on definitions.


Yeah of course it depends on your definition — and in case of such a widely used framework I would usually use type safe with regards to user code, but you are entirely right!

But another point I had in mind is regarding the JVM is that it is strongly typed so such an error will be at least visibly and fail with a proper error message.


Yes, that's the case in Spring.

But there are DI containers which are statically safe, e.g. Dagger.


> Anyone who values static typing will never be in awe of Clojure

I used to value static typing, then I got older.


Can you explain that for the Java noobs here, like me?


Clojure has many functional features and properties that are unique to Lisps and functional languages and aren't shared by Java. Kotlin and Java are much more similar than either to Lisp, so when Java gets more modern features it might cut into Kotlins 'market share' since it makes it less powerful when compared to Java.


Scala and Kotlin tried to be a better Java (i.e. Algol-esque syntax). Clojure tries to be a better Lisp, an entirely different family of language.

Though a good flamewar can be had on 1) Is Scala closer to Pascal 2) Is Pascal Algol-descendant


Clojure tries to be an immutable-data, copy-on-write, threadsafe by default dialect of Lisp, which none of the other Lisps do.


It’s not really copy-on-write though. That implies greater systemic performance losses than what Clojure provides, because it uses HAMTs (Hash Array Mapped Trie) internally. They enable much faster copies of immutable data than copy-on-write does.

Your other points are good ones, I just don’t want people to be put off by copy-on-write performance assumptions. You take a ~50% performance hit from choosing Clojure over Java, but the gains in dev speed, maintenance, robustness, and flexibility to changing requirements are supposed to outweigh that. Especially when you include simpler parallelization, which you covered with thread safety.


There was an issue I ran into with regular Java, JDOM 2 Documents, One thing saves a reference, the other thing snips a piece out to use in the response... oops, the first reference points to a snipped Document. Worse, that behaviour changed without interface changes between JDOM 1 and 2, and it mattered to the code I was editing. Easy enough to fix with a ".clone()" but still... it's not just parallelisation. Immutable data would mean immutable to weird action at a distance like that. Thankfully the unit tests caught it before it could have gone live.

(More precise issue... JDOM 1 lets you splice a piece of A into B while it still is part of A. JDOM 2 insists it be detached first. Detaching it removes it, and that removal can be seen through a reference.)


Huh. I don’t know enough about JDOM to ask more than stupid questions, especially when we’re talking about two different languages on the same VM. So please bear with me or ignore at your leisure.

This all happened within Clojure? Their Java interop is reportedly good, but the docs also say you lose the immutability safeguards when you interact directly with Java classes. I dunno if “regular Java” means literally that, or while working with Java inside of Clojure. Which is it?

“One thing saves a reference” - if this was interop, did you convert that to a Clojure data structure as early as possible, and do no more interop after that until a final return?

I’ve avoided deep interop so far, so I don’t understand the mechanics of the interface, and I’m not clear if your response is about that.


Sorry for being confusing, it was intended as an illustration of why Clojure's approach is good.

Non-immutable data being shared around is basically a recipe for weird action at a distance, is the point.

In what was intended to be a "compatible API, just change the imports" update, code broke because an unchecked invariant became a checked one, and then broke again because the workaround mutated data that was being referred to somewhere else.


I've been bitten by bitwise operations on signed integers a few too many times. Are there any plans on having normal unsigned integer types yet?


I was griping about Java's lack of unsigned types for over 20 years until it dawned on me why mixing signed and unsigned types together in the same language is a bad idea. Consider when one method returns an unsigned type, but another one accepts a signed type. Signed and unsigned types cannot be safely casted to each other without loss of information, and that causes bigger issues.

The only times the lack of unsigned types causes issues is when doing low-level binary encoding, and most programmers steer clear of that anyhow. I think it's a good tradeoff to make things simpler for most, but a bit more difficult for few.


Other than sign-extension on right shift, I can't think of a bitwise operator that has a different implementation between signed/unsigned. What scenarios are you thinking of?


Java has both >> and >>> operators for sign-extending and zero-extending right shift, respectively.


reading and processing bytes mostly. Many people get confused due to the need of bitwise and, yet even if that would be removed I can't quite see the intrinsic benefits, given how difficult would be having another primitive type.


So, when processing bytes, can you name an operation where the existence of an unsigned type would even make a difference? I'm still not seeing it.


java virtually has only 2 integer types int and long, or 4 and 8 bytes one. So all operations are converted to int (or long) 1st, even when they involve byte/short, etc.

Imagine you try to combine a 4bytes (from an array) into a single int. If you just bitwise OR and shift left, e.g. something like

    (b[0] << 24) | (b[1] << 16) | (b[2] << 8) | b[3]
it won't work, you need ((b[1] & 0ff) << 16), etc. The byte values greater than 127 would have the sign bit set (and all other higher bits to make the 2complimentary form)

That part is somewhat alleviated by wrapping byte arrays into ByteBuffers that would the right thing, or even better using only ByteBuffers (preferably the direct version of them when reading from input/output)


Java bytes are signed. They aren't useful when working with unsigned 8-bit values (say, a scaling factor or an index in whatever binary format you are trying to parse). You need a larger integer type, and indeed that bitwise and operation to get the range of values you need.



What exactly do you have in mind. Using '&0xff' is annoying but well whatever. Perhaps 23y ago, applying the bitwise and would be a novelty but now it's a minor inconvenience.

The way java is built in internally I can quite see another primitive type popping up.


Same here. Can be downright confusing sometimes.


Java already has everything you need for unsigned integers doesn't it? You just use the normal int type, and then use methods like Integer#compareUnsigned instead of language operators.


This reads like, "VisualBasic already has everything you need for pointers. You can define your own classes and pass by reference."


Not sure what you mean, sorry?

Are you just objecting to the verbosity of method calls over operators? Yes it's a bit annoying, but the functionality is all there.


It is very easy to mess up signed with unsigned then - because they are the same static type. You have to put comments in the code to clarify the interpretation of bits.


So yes it's also error-prone. But there's no overhead, as the original comment implied.


wait until you learn about signed bytes.


Fantastic article for a language-curious person who doesn’t know Java well.


Most of which unavailable on Android Java flavour, sorry Google's J++.


If you're on Android I'd be programming in Kotlin in any case.


I rather do Android Java and C++ in what concerns my use cases.

https://developer.android.com/games/agdk


Shameless plug: I recently wrote a blog post on Java 17's new features using the implementation of a tiny actor runtime as the running example. If you are interested you can find it here https://evacchi.github.io/java/records/jbang/2021/10/12/lear...


Apparently I mistakenly posted this as a reply...


Is there a reason for that?

I'd really love to get into android development, but I'm loathe to give up some of the nicer language features in Java. What version of Java is android supporting? At least 8, right?


Many got sold on the idea that they adopted OpenJDK, but that is quite far from the truth, so in reality they cherry pick the features and standard library classes/methods that are somehow relevant to port key Java libraries into Android.

Currently they support a Java 11 subset.

You can track them on AOSP Gerrit commits.

Apparently starting with Android 12 , ART can be updated via Play Store and they should provide better updates.

Although there is also the question how they were naive to think Java would never require updates, or possibly related to the Oracle dispute, which anyway proves the point that they behave just like Microsoft did with J++.


Java's supported version on Android is a moot point: all new Android development should be made in Kotlin.


That's not entirely a moot point. Java-the-language is mostly replaced by Kotlin and Java-the-runtime is wholly replaced by ART, there is also the Java standard library. New standard library features, or third-party libraries from the Java ecosystem that rely on new language features like records will not be usable.


Pity that Kotlin builds on Java ecosystem and uses that as selling point for adoption.

Kotlin libraries on the JVM cannot take advantage of JVM ecosystem, and require KMM to be portable across runtimes, or be constrained to the Android Java flavour.

It is like having a flavour of Typescript that only works on Edge.


> Kotlin libraries on the JVM cannot take advantage of JVM ecosystem

This makes literally no sense since Android apps are built using not just Gradle but actually the entire Maven Central repo that all Java apps use.

Without any changes.

If what you said was remotely close to accurate, Android would have its own Maven Central repo.


Without any changes!

Ah the Google's advocate at his best.

Lets not show the people that the libraries that happen to work are those that match Android Java capabilities.

Please show us how to use a possible Java 14 library from Maven Central on Android, using records or Panama interop.


Is there something similar for Python? They are releasing new Python standards with a crazy fast speed. Would be really helpful for me.


https://docs.python.org/3/whatsnew/index.html, each subpage of which has summary highlights.


It would sure be helpful if they took some effort to focus less on adding crazy new stuff every 2 days and instead on maintaining older versions for a change.

Maintaining a python dependant system these days is an absolute nightmare, at least with C++ you know that new compilers will be backwards compatible, gah.


How does this feel with Python being dynamic? I always felt very uneasy writing Python precisely because if some random module became incompatible, especially via a transitive depndency, I might not find out until it was running in production.


What's to be uneasy about? Lock your environments, pin your dependencies, especially in production.


Until your LTS OS gets to end of life and you need to switch to the next one which conveniently deprecates old python versions so you have to rewrite everything.


Why use system Python base environment for production stuff, especially knowing that it will EOL eventually and mayhem may follow? Again, pin your environments using conda, pipenv or whatever else, in which case your base Python version wouldn't matter.


Well if you're using ROS, you get to choose between the two extremes of C++ or Python. It would be great if everything was in C++ to be sure, but ain't nobody got time for that, especially not the cheap results-quickly company I work at.


It's good to see Java starting to waive a lot of the boilerplate. (And frustrating, when stuck on v8.)


A friendly advice: dont be afraid to use public final immutable fields and omit the getters (and setters since you cant have them w/ finals).


I do. It bothers me this wasn't the Java Way from day one. What is with getters and setters - who subclasses and substitutes the implementation of their data carrier objects?


Initially java didn't favor getter and setters. They came with java 1.1 and the attempt bean model (and reflection) to catch visual language designers. The core java packages java.lang; java.util; java.io they dont feature getter/setters, either. If you look at the ancient AWT, there were not getter/setters - they were introduced with 1.1 and the bean stuff, most of the existing methods were deprecated.

So in essence it was the original design, some book authors/design concept promoted it... Personally I have not written 'bean alike classes' for years.


Probably should just use records and get all that for free.


JulianMorrison uses java8, no 'record' there. Not everything fits being a record, either way. The advice is sound for pretty much any class and development, use final fields in the c-tor; make them public if you have to, and dont bother w/ getters.


The “keep readability in mind” tip exposes more Java/OOP icebergs.

  var date = LocalDate.parse("2019-08-13");
  var dayOfWeek = date.getDayOfWeek();
  var dayOfMonth = date.getDayOfMonth();
> The first one is pretty intuitive, the parse method returns a LocalDate object. However, for the next two, you should be a little bit more familiar with the API: dayOfWeek returns a java.time.DayOfWeek, while dayOfMonth simply returns an int.

Java.time.dayOfWeek takes a TextStyle and a Locale, both of which have a list of properties and methods I have to read all about before using. dayOfMonth returns an int, and I can see if it’s 0- or 1-indexed then do what I want with it from there. Why does DayOfWeek have a class instead of being a function that I can throw a number at like dayOfMonth?

I’m also skeptical about Java.LocalDate being “pretty intuitive,” having worked on local vs server vs data-store timestamps, as most of us have. At least LocalDate doesn’t have mutable setter methods like other Java dates…


>Why does DayOfWeek have a class instead...

B/c in most of the world, the 1st day of the week is Monday, unlike the US where it happens to be Sunday. Having it enum is a pretty decent choice.


Sure, someone’s got to make a numbering choice somewhere for the canonical value. Same thing goes for timestamps, and we settled on UTC, so all offsets can be calculated given that.

Once you know that “Java says X is the first day of the week,” you can make your own calculations from it with modulo arithmetic. There is no need for a class here, or for it to build a whole subsystem of locale interpretations. That should be the responsibility of a library. Maybe even the standard library, but it should not mask the core data values in the system by default.

dayOfWeek(n) should be something that you apply to a value, not something that returns a value with no args based on your locale. Or, if it does, the function itself should accept an override locale instead of assuming it from a higher level.

Timestamps are hard and maybe not the best example. It was what’s in the article though, and I’m also pretty salty about them after some recent work stuff.


> Once you know that “Java says X is the first day of the week,”

The basic assumption when designing anything is that you can never rely on assuming that other humans "will" know.


A funny part is that java has java.util.Calendar that has setFirstDayOfWeek(int), the default depends on the locale used to create the Calendar. It's considered a bad design overall, esp with SUNDAY const being 1. For most of the world that (sun=1) made absolutely no sense at all and assumed one of those weird things like jokes about IQ and room temperature.


> java.util.Calendar that has setFirstDayOfWeek(int), the default depends on the locale used to create the Calendar

That is exactly the kind of hidden assumption that I’m trying to fight against! If the date and the locale were both passed in as parameters, preferably just plain data, this would be a transparent function. Instead, you have to find and read through the documentation to see what kind of mischief is going on. And you have to do that every time you encounter a Java.until.Calendar.

Edit to clarify: if “locale” is some class, you have to learn that class DSL to examine it, and depend on a runtime debugger that does the same. If “locale” is just a hash map, you can examine the values without a lot of faff.


Sure, but how far does that go?

If I try to assign an int value to “3.2” will the language silently fail with null, or a round, or a floor, or a ceiling? Some level of understanding is necessary. I want it to also be reasonably explicit.

Java time library doesn’t meet that standard until Instant.


> If I try to assign an int value to “3.2” will the language silently fail with null, or a round, or a floor, or a ceiling?

one may argue that the only correct thing to do here is a compile-time error


Because dates are actually complicated, and so is the real world. You may be content in having just an int to display Sun 24-10-2021 to your users, but there are needs to get the actual weekday. From locale differences (US has the start of week on Sunday, most of the world on Monday), to locale differences (Country X nuked 43 days on year Y, your basic algorithm doesn't handle that because it's not working with tz-data, and it's broken now), to locale differences (how do you plan on displaying it? Short form? Translated?), to infinitely more date fuckeries that _have_ to be handled if you want your Date API to be credible.


Yeah. See my reply to your sibling comment: timestamps suck and it was maybe a foolhardy example that I’m not competent enough to defend. Here goes me trying to make my case anyway! =)

My big assumption is that if a system cares about global timestamps, it should go all the way. If you have a “canonical system timestamp,” which I think is necessary, then you can operate on it functionally to get whatever local display that you need out of it. Your example with Country X means that dayOfWeek() AND dayOfMonth() are already both broken unless/until the language itself makes an emergency update to account for the change.

With dayOfMonth() I could substitute another function that handles the change. With dayOfWeek() I’d have to override the whole class, if possible.

I have seen this in action. A company I worked for was stuck with using outdated time libraries in Java because of IBM WebSphere (1.7? 1.8?). Those didn’t match the more recent libraries in Javascript (moment.js) and the differences caused significant problems, since the same locale keys had different time offsets.


Why are you blaming a class called “LocalDate” for not giving you global timestamps? The java doc for the class has an entire paragraph on how it is not a time. If you want to model a specific instant in time, use Instant.


This is a fair criticism. Java Instant should be the standard, and anything that doesn’t use it as its base should be deprecated. I have to support older JDKs, so I don’t know. The article starts with Java 9, and Instant was included with 8, so it is not a fault of the article.


I’ll gladly grant that the mutable date types of pre-jdk8 Java were ugly APIs, but you’re still missing my point here: the LocalDate class was added in the same release as Instant was—it’s not a worse Instant, it’s solving a different problem.


I guess what I’m aiming for (and perhaps not hitting the mark with) is that LocalDate’s 9 static constructors should be reduced to a single constructor that takes an Instant. If Instant does some translation from “whatever -> Instant”, it should do all the translation.

If you want something like a birthday, you’re already making assumptions about the beginning and end of that day. Ask a 21-year old from Australia about getting into USA bars on their birthday. It’s a system-specific decision on how to handle those things. Probably more relevant to finance, but the underlying principle is the same: dates without timestamps are ultimately derived from or interpreted with dates with timestamps, and hiding that fact too early in an API (like LocalDate might do) is a bad abstraction.


The whole point of a local date is that it is just a container for three values - year, month and day, and nothing more than that. You cannot go from `Instant` to `LocalDate` without specifying time zone/offset information, and vice versa.

“Local date” is a useful concept on its own, and it is not at all always derived from an absolute instant of time. Some examples:

1. Date in a file/directory name, which often happens with big data processing. There is no way to tie these dates to some absolute frame of reference without external information, but it is often not really necessary, and there is still a need to work with these values as dates, e.g. compare them or compute offsets.

2. Anything which is tied to the user’s local time frame, e.g. alarms. They simply cannot be tied to some absolute time reference, because they always represent some time wherever the user is located right now. Even birthday is something like this, because for the purposes of celebration, people care about the specific calendar day, not the actual moment of time in whatever absolute time scale there is (at least, most of the people I know think like that!)


I’m stuck on 8 and 11 in my current projects, so I haven’t seen much of these in the field other than the new record feature. What I am really surprised about is the var construct. Honestly, this leaves me a little conflicted since it seems to be antithetical to the principles of a strongly-typed language. So does this now reclassify Java as a weakly- or dynamically-typed language? I would perhaps argue against it being dynamically-typed since by definition type inference happens at runtime, but it certainly leaves Java in a sort of limbo. I don’t personally care, but I know at some point the topic will come up amongst my dev peers, at which point there will sure to be a (mostly friendly) debate.


> seems to be antithetical to the principles of a strongly-typed language. So does this now reclassify Java as a weakly- or dynamically-typed language?

Var and type inference are neither antithetical to static typing nor do they make a language dynamically typed. They're antithetical to the verboseness of Java.


Java is not usually considered to be a statically-typed language and I was not implying that it was. And the definition of dynamic typing is somewhat vague to me, although allowing unspecified type inference is somewhat dynamic, even though one cannot reassign the variable to a different type at a later point.

And my point about was that Java was designed to be verbose, except that now “var” breaks this verboseness. I am sure there was plenty debate on this topic already, but I thought it would be interesting to reason about how this changes the character and perception of Java.


> Java is not usually considered to be a statically-typed language

Java is absolutely considered a statically typed language. Compare it to Python and CL, then you know what the difference is.


Except that having the type info is really, really useful at the call site. I know the IDE is able to infer it and display it but it's still a pain to see all the vars during a code review in a browser.


Agree, it's a straight up reduction in code readability for all future maintainers so a single original developer can avoid doing a thing their IDE probably would do for them automatically. (I use "introduce variable" to create most variables automatically of the correct type).

Yes I know the IDE can also reveal what type a var is, but you generally read code in lots of places that aren't IDE enabled.

It also adds more work for the compiler. I'm not sure the effect on Java compile types yet but Kotlin and Swift have both suffered from slow compile times and this seems to be a factor.

I'm guessing that most of the larger codebases out there will add "no var allowed" to their style guides within the next few years.


It’s not an either-or thing though. I prefer defaulting to vars and changing specific variable definitions to show explicit types based on the right hand side expression. These “no var allowed” and friend global rules are almost always bad (looking at the myriad c++ rules) —- there are cases where it will make code more readable and others where the traditional style is the preferred.


var shouldn’t increase compilation time in any meaningful way. The compiler has to figure out the type anyway (otherwise, how would you get type errors?).


Yeah I'm not sure where this originated (recent C++ has something similar with auto and I think C# now has var as well) but if I'm doing a code review rather than writing code in an IDE, var is a bit of a hindrance.


It's not the IDE, it's the compiler that infers it. So type information is always available.


That's not the point - try having 2 or 3 way diff, pull request with just var. It's =awful=. I know most of the standard APIs (incl. overloaded methods and what not) by heart but not every little function and class in a million lines of code project.

To me var adding is mosttly an anti-feature - when using an IDE, I dont even bother to declare the stuff, it gets auto completed. When I read code outside the IDE - well it sucks. It's not the level of 'perl - write only', but at least it aspires to be.


It’s just “simple” type inference.

As for what style is preferred: yeah I have also seen plenty of not so friendly debate on it, but I think it is fairly simple (and we do have “prior art”, with Scala Haskell, etc): in case the type is trivial, use var. Like new SomeLongAssClassName is a perfect candidate for var as having the typename twice doesn’t constitute any additional information. I would wager that with good method names, it can also be used for things like `obj.getLocation()`, where we happen to have a Location class as well. The other use case is when we just don’t care about the type, either because it is very complex with low info density (like a Map with a nested generic value type, which is easy to recognize from context and variable names), or because it is trivial (like I like to use things like var i = 2)


Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: