it consistently works and has a huge ecosystem, but "beautiful" is never a word I would use to describe java. off the top of my head:
* no type level concept of a const object (ie, you can have a const reference to a List, but never a reference to a const list). this makes const-ness an implementation detail of the class itself! so frustrating that List:add() can throw depending on the underlying class.
* lack of tuples (and no, record doesn't count). this is just a syntactic sugar thing, but I really miss it from c++ and python.
* var is far less powerful than c++ auto.
in most cases, I actually prefer the syntax of c++, which is really saying something.
The missing constness is the biggest flaw of them all, in my opinion, especially when reading other people's code. Always having to dig deep to make sure an object isn't altered somewhere down the line gets old really quick.
I've just taken a new job writing primarily Java whereas I was previously writing mainly python and typescript.
One of the first things I've noticed is how dead Java's ecosystem (Maven central) seems in comparison to other ecosystems like PyPI, NPM or Cargo.
(Also side note: I've published packages on each of these registries and the publishing process for Maven central is comically terrible! This has to be discouraging people from contributing to the ecosystem.)
probably depends on what kind of stuff you're working on. I mostly build web services and data pipelines on AWS, where java is still the best supported language (even if others have joined the list of officially recommended).
java might not see the same brisk pace in library development as other languages, but it's also 30 years old. aside from core issues with the language that can't be papered over by 3P libs, what's missing?
> The language itself is quite beautiful when used properly and with modern features.
I respect your opinion but I wouldn't call Java beautiful (of course it depends on your definition of beautiful). It takes so much ceremony to do things you would do without any thought in other languages .
Initiating a mutable set
Python
`a = {1,2}`
What most Java programmers do
```
var a = new HashSet<>();
a.add(1);
a.add(2);
```
Shorter one but requires more ceremony, and hence knowledge of more language semantics
```
var a = new HashSet<>(new Arraylist<>(List.of(1,2))
```
I don't know if the above works but the Idea is to initiate a list and pass it into a HashSet constructor.
Similarly Java 21 allows you to model union types but doing so requires you to define N+1 classes where N is the number of union cases whereas in other languages it's as simple as `type A = C |D`
In python, those give a mutable set, which is what I was referring to above.
Also not to put too a fine a point, you'll never see python code like that in the wild, but even in code reviews these days, it's common to find Java code written like in my example because the syntax for sets in Java came after Java 8
My bad on the mutability. As far as code like that written in Java, I don't think I've ever seen something like that in anything non-toy and I started with Java 7.
I think that's fair. As with good UI, I appreciate when I can navigate via intuition. The solution in Python is intuitive, readable, and memorable. When working in Java, I frequently felt compelled to read and review pages of docs in search of a straightforward solution. I'm a sucker for Python's syntactic sugar.
I've never seen a language have a single way to do something. It's part of Python's mission statement, but it doesn't appear to even have been a consideration in practice.
> The language itself is quite beautiful when used properly and with modern features.
> It just really needs a makeover and better tools.
I love java, wouldn't call it beautiful though. But I don't need a language to be beautiful, I need it to be pragmatic, blisteringly fast, have an extensive ecosystem and top quality tooling. Java delivers #1 on all of those so I love it.
For beauty, I like ruby and lisp, but those both fail on all the other criteria so they are mostly for hobby use for me. (Python, the darling of everyone these days, is pretty much dead last on every criteria except popularity.)
> better tools
I'd say Java & JVM has pretty much the best tooling on all fronts.
I can't think of anything that has better tooling around the language and runtime.
I can't see why you'd pick Java over C#. IMO its basically all the same preferences with slightly more consistent syntax choices. That said, Java is great too. It's underrated considering what a workhorse it is.
Well the ecosystem is different than the language but you can certainly find 3+ of any library you need in Java where .NET usually has a single choice.
I use Jetbrains for both Java and C# and the experience is essentially the same.
I am a big functional programming geek. I am one of the few people on the planet who can honestly say I have been paid to write F#, Haskell, Clojure, and Erlang. I have spoken at FP conferences like six or seven times, and I have shit on Java for most of my career.
And yet, my latest talk at Lambda Days basically boiled down to “Java 21 and later don’t actually suck anymore”, and I genuinely do mean that.
Java 21 is actually fun to write, even for a grumpy FP advocate like me. Virtual threads make concurrency a lot simpler, and now that there’s proper records and ADTs (in the form of sealed interfaces), along with pattern matching, the language is actually pleasant to use. I haven’t dived into 25 yet, but I suspect I will like it as much or more than 21.
The biggest issue, though, is that Java programmers won’t use the new features. It was like pulling teeth at my last job to get people to use stuff from Java 8 (e.g. the `var` keyword), and none of my coworkers even knew how to use NIO or BlockingQueues which I think predate agriculture. I mean, hell, I had explain what “fairness” was to engineers when using a ReentrantLock because someone “corrected” my code with `synchronized`.
I don’t think Java makes people into bad programmers, but I do think it selection-biases for intellectually unambitious engineers. They learn exactly enough Java in college to pass their courses, and then get a job at a BigCo that doesn’t strictly require ever learning anything more than what they were taught in their “intro to data structures” course.
I have met some extremely intelligent Java engineers who do have intellectual curiosity, so I am not saying it affects everyone, but I do think that they are the minority. Java 25 might add every feature to make my wildest dream come true but it won’t matter if I am not allowed to use it.
> I don’t think Java makes people into bad programmers, but I do think it selection-biases for intellectually unambitious engineers. They learn exactly enough Java in college to pass their courses, and then get a job at a BigCo that doesn’t strictly require ever learning anything more than what they were taught in their “intro to data structures” course.
I think that's a fair comment, but also there's this perspective: I first touched Java 1.1 in 1997 in college, and only for a semester. Then for the next 22 years never looked at a line of Java, working mostly in C++ and Python plus dabbling in FORTRAN for high performance stuff that needed to be written there. I generally consider my self not intellectually unambitious.
Then I moved to a Java shop who specifically needed high performance math (well at least as high performance as you can get in Java, which is actually pretty good now). But we stick to Java 8 compatibility because we have some BIG customers who require that because of LTS on Java 8. There are some nice constructs that would help make the code more readable and modern, but when you need to support people and actually make money you do what you need to.
Sure, I am not claiming that you have to use every new feature every day 100% of the time. Obviously there are cases where you can’t upgrade for legal or compliance or “customer is just being difficult” reasons.
A lot of Java jobs aren’t that though, especially internal applications. A lot of places are running Java 17 or Java 21 on all their servers, literally have no plans to ever support anything lower, but the engineers are still writing Java like it’s 2003. That is what’s maddening to me.
I think it has a lot to do with work culture. Many tend to mimic what others are doing in order to not stick out.
At my previous job some were able to change that by consistently using "modern" features of Java. It inspired others to change and eventually we ended up with a good code base.
Be the one to start the change by implementing new features using good code. This will give others "permission" to do the same. Also try to give soft suggestions in code reviews or pair programming of simpler ways to do it (don't push too hard)
At my current job all of us were eager to try the latest features from the start, so we never had to convince new hires.
I know I came off as a bit negative, but in fairness to them, they did more or less continue working on what I was doing using the newer Java 21 features, and after I got a few pretty interesting changes merged in some of the more junior engineers started using them too; particularly I was able to successfully evangelize against the use of `synchronized` in most cases [2] and got at least some people using queues to synchronize between threads.
It honestly has gotten a fair bit easier for me since I've been doing this for awhile; at my last job I was the most experienced person on my direct team (including my manager) and one of the more experienced software people at the company, so I was able to throw my weight around a bit more and do stuff how I wanted. I tried not to be a complete jerk about it; there were plenty of times people would push back on what I was doing and I would think about it and agree that they were probably right, but I outwardly rejected arguments that seemed to be based on "I didn't learn this in university so it's wrong".
I have had other jobs (at much bigger companies) where they were not amenable to this. I would try and use new features and my PRs would be rejected as a result, usually with some vague wording of "this way is faster", which I later found out was (as far as I can tell) always a lie.
[1] It is not hard to find my job history but I politely ask you do not post it here.
[2] I'm sure someone here can give me a contrived example of where `synchronized` makes sense but if you need mutexes I think you're almost always better off with a ReadWriteLock or ReentrantLock.
Programming language is a tool. Java developers value stability and ease of understanding for the code. I've seen the nice features you complain that don't get used, reality is that nobody wants to waste time knowing them unless they are intuitive to use. Especially when are forcing to use newer JDKs.
There is no value in solving a challenge in a way that only you understand or make others lose time trying to understand the logic.
Java 8 was the peak of development age for the JDK. Everything that came after isn't really memorable nor helpful, especially lambdas. You mention "var", why would we ever want in Java to hold a variable that you can't read immediately what is the type? That is a source of bugs and time waste to track down what object is being used.
I don't mind you are happy with all these changes, just remember that we absolutely don't care about them nor will make much of an effort because in the end of the day we don't want to follow the same route of other programming languages unable to handle gigantic and complex platform systems.
This isn't a competition to showoff who can apply new tricks, we absolutely don't care about functional programming. Java code must be simple and easy for anyone to read, that's it.
The new features make the code easier to read. For example var:
List<Account> accounts = List.of(new Account(1), new Account(2));
var accounts = List.of(new Account(1), new Account(2));
It just reduces visual noise and boilerplate that you already know.
Java 8 is also a slow and old runtime. It performs terribly in 2025. Here’s a quote from 2020 and the gap has only gotten wider [0]:
> JDK 8 is an antiquated runtime. The default Parallel collector enters huge Full GC pauses and the G1, although having less frequent Full GCs, is stuck in an old version that uses just one thread to perform it, resulting in even longer pauses. Even on a moderate heap of 12 GB, the pauses were exceeding 20 seconds for Parallel and a full minute for G1. The ConcurrentMarkSweep collector is strictly worse than G1 in all scenarios, and its failure mode are multi-minute Full GC pauses.
You’re doing a disservice to everyone by continuing to use and glorify it.
Using Java 8 doesn't mean using the java 8 JDK. It just means not using the newer features. I don't see anything in the parents post about restricting which JDK they use.
I agree, and I'm not sure how something like 'var accounts = calculateAccounts(something)' can be thought of as better in a code review setting. I suspect using "var" or equivalent will be considered a problem by most companies within the next few years.
Those writing in python and javascript don't honestly know any better. They grew in a world without unit testing or without products that need to grow into gigantic systems maintained over the next decades.
This reminds me of a developer writing in jRuby because "it was better". While he was in the company he'd still give support to his own works, after leaving nobody else wanted to pickup those "better" things and would prefer to write workarounds to the tool, it effectively became a black box that few could improve and even worse to test. As result, those portions had to be rewritten in proper Java so that we'd be able to deeply measure/test and improve.
There’s nuance in programming. Both of these statements can be true. Var reduces boiler plate when you’re duplicating information in both the lvalue and rvalue.
I’m in agreement that when the information isn’t in the rvalue that you shouldn’t use var.
Var can help in maintenance. Change the return type of calculateAccounts and you don't have to modify this code (assuming that it duck-types out equivalently).
That isn't necessarily a huge win: it does force a compile change that isn't obvious from the source. And a refactoring tool could have performed the code change automatically so it's not as big a deal as it might have been.
I would argue that it helps me when performing maintenance to see and correct where types may have changed. Not always, and sometimes it is busy work I agree, but overall I prefer it.
Yeah I generally avoid using 'var' to elide method return types for that reason. In my early phase of var enthusiasm I even had it result in a bug that would have been caught if the type had been made explicit.
I do still really like it in the `var list = new ArrayList<String>()` case though.
Modify it when? If the interface renames? IDE does that. If the actual return type changes? They you do have to check that place whether it is still valid.
Maintenance involves a lot of reading of unknown code. That is literally the situation where you are rewiting var to specific types to figure out what is going on.
I tend to agree here, I prefer explicitness. Even though yes it does mean some pain when refactoring, it also creates very clear diffs showing impact, which I consider a positive.
Ideally this could all be dealt with via tools. An IDE that shows whatever the user prefers, but is actually saving into a format defined for the repo.
It is less readable because either accounts is named incorrectly or calculateAccounts is.
If it should be createAccounts, nothing is lost unless you have multiple account types in which case you would call it createUserAccounts, create adminAccounts etc.
Please point me to an objective measure of "readability" that holds for all people. And then demonstrate that your example is "literally" lower on that scale.
Readability was a term coined by people writing languages that are litterally unusable without an IDE to hide stuff when they render the source file to explain why refusing to learn languages with curly braces is sane.
Oh man.. that comment really hit deep. I'll never understand why people argue that it is "easier" to adopt invisible characters and exact tab positioning instead of just using braces or similar to mark the code blocks.
Drives me nuts because you really can only write code for those programs with an IDE that is prepared for the case. Try to edit the code directly from the github web interface and it won't compile.
Whoops, my initial intent was "without curly braces", since that's where most of the readability comments are from. I guess the comment works both ways, though.
> Drives me nuts because you really can only write code for those programs with an IDE that is prepared for the case.
Usually people have strict rules for that (e.g. pep8), and most text editors will do it for you with little configuration.
It allows you to limit `items` usage to `Map` supertype and allows you to swap implementation easily, if necessary. `var` is weird feature, because it allows people to use implementation-specific methods methods and tie code to a single implementation, essentially making it less agile.
There are valid use-cases for `var`, but IMO this feature should not have been added to the language, as it's too dangerous and most people won't use it responsibly.
Your example isn’t good though; var is only for local variables, which should be perfectly allowed to use implementation-specific methods.
For argument types you don’t have var, so methods that take in a map can stay abstract and you can still pass in the implementation-specific version into those without casting it to the interface.
ETA:
I guess I am trying to say that if you want to be abstract, they should be at the argument or properties level. Local variables should be used locally. I agree that generally speaking you should try and prefer using Map for anything shared across different parts of code but I am not convinced it’s bad to have var be implementation-specific; if your method is big enough to where swapping out implementations like this will take a lot of time, your method is probably too big anyway.
No, you shouldn't write that. Your variable should represent the complete implementation. Where you use it should be as generic as possible, for example, the parameter in a function call.
Why would you want that? The whole point of an interface or superclass is to let you swap implementations without changing everything.
I also like the nudge it gives you to use only the higher level methods, rather than the ones specific to the subclass unless they're needed. That also improves flexibility.
It’s not about swapping implementations, like the original poster suggested, so that you can later come in and swap out different implementations in a local sense. Interfaces are used to decouple parts of an application; for example mocking test interfaces. Another way to look at it is that interfaces are a contract that needs to be fulfilled.
Declaring a local variable as an interface to hide functionality so you can swap out functionality later is misunderstanding the fundamental theories around interfaces. Your variable should be whatever the function or method returns. If you want to abstract the type so it can be swapped out, create a function that returns an abstract type. Don’t tell the HashMap constructor it did the wrong thing. Now that I think about it, I’d recommend you use “var” in all cases, and not try to redefine the return values from a function.
Using var there doesn’t leak any implementation details into any contracts. Var is for local variables inside of functions. There are no details or contracts there.
You could have as easily used String instead of var and there would be no ambiguity. This gets worse for numbers where it is important to know if you are dealing with an integer, long or something even bigger.
I agree with you on switches and do like the """ feature, thought. It was a real pain in the rear to include the multiple + "\n" back in the old days. This is a very clean and intuitive improvement.
> You mention "var", why would we ever want in Java to hold a variable that you can't read immediately what is the type?
I've been a full time java developer for the past 7 years. Let me start by agreeing that I have very little interests in the functional "innovations" they added. They're fine, but most of my colleagues agree that code using streams or lambdas very quickly becomes harder to debug then if you just wrote the code with loops and.
That's far from true for every java feature though. Switch statements have been super cool, Green threads are a promising road out of the CompletionStage hell the children are dreaming of these days. "var" is a very nice way to reduce double typing ("Thing x = new Thing()" -> "var x = new Thing()") and also a nice way to avoid changes to unrelated files ("Thing x = getFoo(); f(x);" -> "var x = getFoo(); f(x)" means changing the name of class Thing doesn't require any change to the code) that's been helpful in a lot of cases.
Yes, please don't get me wrong. There are always a few good things, but like you mention with streams: the person writing it can have an idea of what he was doing but the others afterwards will struggle to make changes and will likely revert the code base to simpler code.
Switch statements got better. I'm also getting used to Paths albeit don't yet understand why File wasn't already good enough. It is difficult to find compelling reasons for upgrading the JDK since I know it will be hassle for everyone that later down the line has to install/run the product.
If I had to guess, I'd say that a vastly bigger problem in the world is legacy code written in decades-old obsolete and inconvenient language dialects than code that's "too new".
I do not want to be rude but I am going to be: this entire comment illustrates my point very well. Instead of trying to actually learn new stuff, you say that the language peaked thirteen years ago.
> Everything that came after isn't really memorable nor helpful, especially lambdas.
Lambdas came out in Java 8, along with the streams API, and the fact that you don’t think they’re useful more demonstrates to me that you don’t actually understand it, since nearly every language before and after Java has lambdas and nearly everyone agrees that they’re useful.
Reifying a bunch of temporary interfaces is not “more readable” than a lambda. A bunch of terrible nested for-loop logic is not “more readable” than the streams API.
> You mention "var", why would we ever want in Java to hold a variable that you can't read immediately what is the type?
Your IDE can show the type, but even disregarding that there are lots of cases where you have to write the type twice in Java and it just makes noise. It doesn’t make the code more readable.
> we don't want to follow the same route of other programming languages unable to handle gigantic and complex platform systems.
Burying your head in the sand and Ignoring improvements in the language doesn’t make you more able to handle complex problems. It actually does the opposite and that’s so plainly obvious that I don’t think you actually thought through the sentence before you wrote it.
Take something like virtual threads. Most Java programmers don’t use them and instead keep using an executor service incorrectly because they also never learned the difference between blocking and non blocking IO. For them, virtual threads would be strictly better because it properly parks blocking IO.
Ultimately, I guess I disrespectfully disagree that Java “peaked” in 2012.
Dude, java 8's eol was 8 6 years ago, now. I have nothing gainst waiting a bit for "newer JDKs", but way too often the pattern is that teams use the oldest possible JDK and only migrate several months/years after the last possible vendor has sunset their support.
> Java 8 was the peak of development age for the JDK.
To me it looks it was merely the point where your stopped caring.
I view this largely as a symptom of the widescale “success” of the bloated J2EE app servers in the early 2000s to mid 2010s. Your Java version and dependencies were locked in and upgrading was a massive effort. A large group of developers stagnated on Java 1.4.2 and 5 and seemingly never updated their use of the language, even when moving to Java 8 and beyond. The legacy stuff keeps ticking along.
I kind of think thats backwards. I think the success of the abomination that we have decided to label as J2EE or Jakarta was attractive to intellectually unambitious Java engineers because it progressed so slowly.
I think people here are really underestimating how intellectually lazy most people are at their jobs. HN selection-biased for a geekier crowd so a lot of my criticisms don’t apply to readers of this forum.
Sorry, but I don't think a preference for slow evolution is always because of laziness. What's wrong with wanting to keep improving on a skill instead of having to waste time relearning things every six months?
Software is rare among arts/crafts/whatever in that it is difficult to find nice areas of software to keep digging deeper into (as curious people do!) rather than having to move on to something new just when you start to be good something.
It's not even wanting to focus on depth instead of breadth, as the constant changes means you are barely able to keep using your older skills, so there is little actual breadth more like constantly moving between shallow pools of knowledge. Maybe it feels great to constantly be moving, but I do not see how it is productive or positive in any way for us.
I am arguing that they aren’t improving their skills in any regard, including how to properly use the tools they already pretend to know. They don’t go “deep” or “broad”, they don’t learn anything more than what they were taught in university.
I have had to debug a lot of concurrent Java, so my opinions are skewed towards that, but I have seen cases where “staff engineers” label every single function as `synchronized`, who genuinely don’t know why you would use a queue, have no concept of thread starvation or fairness, when to use an atomic instead of a mutex, and genuinely do not seem to understand that for network applications it’s generally more important to figure out how to reduce latency than trying to choose the optimal hashmap or sort implementation. These aren’t arcane details that require a PhD in category theory or require being constantly plugged into hacker news, these are extremely basic things that these Java engineers do not know.
I think most Java engineers, like more professionals in general, are very bad at their jobs. I think Java just selection-biases higher than other languages for people who are bad at their jobs.
Yeah, people often underestimate the importance of community when they discuss programming language. In fairness to the Java crowd, you rarely see codebases looking like the team was trying to fit every new concept from some blog they read somewhere in the codebase, like you do in e.g. Scala or Haskell.
I generally hate when people use the “magic” excuse for not doing things. Most of these tools are open source and/or can be readily viewed in IntelliJ. It really isn’t hard…cmd+click on what you want to look at.
If you’re an engineer you should be able to easily read the code to see what’s going on. Most of the time the “magic” can be understood in less than 30 minutes and then it’s not magic anymore.
You're right, "magic" is generally when someone doesn't take the time to understand things. A busy CTO may not have time, so more things seem like magic to them.
What's inexcusable is to inflict the rest of your team with your nonsense.
I got in a bit of a disagreement with teammates at a previous job. I liked these teammates, they were very smart and nice people, but for a specific project I wanted to use LMAX Disruptor, and the excuse for not using it was very literally “we don’t want to make people learn anything”.
That stuff blows my mind; aren’t we engineers? If we’re not willing to learn new things and adapt, what exactly are we offering that a high school kid who bought an “Intro to Java” and “Intro to Spring” book can’t?
I wouldn't dare say XML is better in this regard, but a good reason to be conservative with the use of annotations is exactly that cmd+clicking them doesn't easily lead to where the behavior is implemented.
I thankfully have not had to use Java for years, but at one of my first jobs after grad school I was hired because I had Kotlin experience and they were moving the code base away from Java.
A few of the older developers also complained about the use of map, filter, zip, lambdas, etc being harder to read as well. Then a month or two later when they realized they weren't going away, it was an important part of the language, and just learned how to use them the complaints just one day stopped.
Except for when we had to touch Java code and it didn't make sense to convert it fully to Kotlin.
This is precisely my point. For a lot of Java programmers anything that they didn’t learn in university is “bad”, as far as I can tell, purely because they don’t want to actually learn new things.
They’ll give half-hearted justifications that are usually reductive or just flat out lies [1], but ultimately it seems to boil down to new=bad.
The amount of terrible code I have had to debug because Java programmers haven’t figured out you can use queues is upsetting, because all they learned in university is how to use `synchronized` wrong.
[1] I have learned that nearly every time someone says their disgusting code is “faster”, even when they claimed they tested it, it is almost universally untrue when I write a microbenchmark to check it.
That's called selection bias for both of you. Reddit will expel anyone whose opinion does not correspond to the local mob bias. Workplaces select people who are similar to already working ones.
- how’s the environment? Build tools, dependency management, etc. it used to be a PitA back then.
- how has the typing system and generics evolved to support this? Have they introduced any type of variance?
IntelliJ IDEA is genuinely great. It helps that they were the ones who developed Kotlin, and a fair bit of the actual language changes were gacked from Kotlin. (Or you could say "prototyped and shown valuable in Kotlin".)
They are still hampered by lousy nullable support.
You didn't ask, but Spring still sucks. It's not part of the language but it's a ubiquitous framework.
It’s amazing that Java has access to really great, high performing web toolkits like Vert.x and even Play, and yet the entire enterprise Java world has converged on Jakarta and Spring. I don’t like Spring but in my mind there is a special place in hell for Jakarta/JavaEE.
Generics still kind of suck but they’re workable enough. As far as I am aware they’re broadly unchanged and I don’t think they added any kind of new variance.
It turns out, though, it’s still good enough for sealed interfaces and the like; I don’t have too many issues with it, though that might just be Stockholm syndrome at this point.
Maven is terrible as always but Gradle is generally fine. I use IntelliJ community edition solely for Java and it works well enough for what I need.
It’s not like Java is going to replace F# for me or anything, but I do genuinely think it’s more fun to write now than it used to be.
Maven is generally fine. The only complaint anyone has is “xml bad”. Outside of that maven is probably (?) better than gradle. At least it prevents a custom scripting mess.
I haven’t touched much modern JS. I have done some TypeScript in the last few years but of course that’s a transpiled thing. I think TS isn’t too bad to write functional stuff though I have no idea if people actually enjoy reading my code.
The explanation on why Java lets you use unnamed variables to prevent accidental use during deconstruction, but those unusuable variables still getting initialized by calling accessors, is a perfect example why I dislike the way Java implements improvements.
Every single, logical step that led to this hidden performance problem makes complete sense, because every improvement had to be its own tiny, contained improvement, but the end result is that `case Foo(int _, String _, User _, int age)` will still call three getters _just in case_ you're abusing the language to add side effects to accessors.
Perhaps even worse is the explanation that follows: if you don't mess up your accessors, the JVM _may_ decide to not call those accessors at runtime. So now the language itself has this weird performance impact to maintain backwards compatibility, but at runtime that backwards compatibility _may_ not exist and provide you with a performance improvement instead, negating the whole reason why backwards compatibility was added in the first place.
I like the improvements to Java, don't get me wrong. It's no longer the JDK 1.7 language poor enterprise programmers are stuck with. But if the Java people had come together and worked this out as one single feature, rather than five different ones, we wouldn't have needed to remember edge cases/a code analysis tool to remind us that using this intuitive language feature _may_ actually has a 3x performance impact depending on the mood the JVM is in today.
I’ve worked in Java for over 20 years. Being the “lingua franca” of the enterprise is its biggest strength IMO, but it is also perplexing to me that it was able to do that in the first place. The language itself is not bad. The tooling around it is very good. But the codebases you encounter written in it, particularly in the enterprise, are often horrible.
Mind sharing why do you consider Java tooling to be good (and largely, what is good here)?
The reason I ask is that I recently had to join a Java project at my company, and having a background in Node/Rust/Perl/Lua and some C++, I found the Java tooling to be extremely unsuitable for my taste.
A simple example: there is no standard LSP server, and the amount of jumps required to have a working setup with FOSS tools and make it IDE-independent is just horrendous. In every other ecosystem I've worked with so far, it was pretty easy in the last 5 years: if you don't like IDEs, you can keep using your vim/emacs/helix or whatever and just embed a plugin or two, with LSP integrated -- and you're ready to go.
Java world felt complete the opposite, like you had to use/buy some commercial tools to start doing something.
Tooling goes way beyond the editor/IDE. Eclipse is a very good free option. As is IntelliJ CE. I personally have the all products pack and use the ultimate version.
Beyond the IDE you also have to consider the build tools, package management, debuggers, profilers, static analysis tools, etc.
It’s honestly too much for an HN comment. But as an example, if I do open one of these awful projects at work and it uses gradle for example, intellij will understand that, import the project, get all dependencies, let me run any target with debugging or profiling, give me code coverage, etc.
It's a fair note about tooling in general, I started with the code editing because it's the first thing before you can taste and judge the rest.
I think my frustration comes from the fact that in most other ecosystem I can use the tools I like, but in Java I have to use things like Intellij.
Intellij CE may be open source, but it is entirely owned by a private business whose primary goal is to sell their product - which affects how well are the integrated, open to accept community feedback, etc.
There are several open source tools for Java (Eclipse, Visual Studio plugins, Netbeans and others).
The reason I don't use them is not because they are bad, but because IntelliJ is so much better.
I even use IntelliJ Ultimate for non Java code like React, even though Visual Studio Code seems to be de-facto standard for React developers and guides.
I guess it's just a given that everyone uses IntelliJ Ultimate. If you don't, I guess you're right, the tooling is lacking outside of that. But it's so worth it...
Having now experienced such Java codebases for the first time in my 15 year career… no, the Java code is much, much worse. Over engineered and otherwise poorly written Java code is SO much worse than other over engineered code I have worked with.
Honestly for the limited projects I've had to work with... I can attest to that. It's the over engineered abstractions that get me. Traditional Java EE users use WAY too many abstractions and interfaces, it's absolutely horrible to debug.
Same here, plus sealed classes - a very powerful feature.
What I actually haven’t used up to this point are VTs. I got a service that implements a job queue and it currently works flawlessly with scheduled executor pool. I’m reluctant to go with VTs before evaluating what implications that may have.
I think VT's are only going to be interesting if you need 1000s of them doing tiny amount of small work often.
For me I have been waiting for them for 20 years, as the blocking IO made MMO development impossible.
In the meantime we got NIO in 1.4 that only became performant and stable around 1.7 so it's been a slow progress.
I dug out my old blocking server code from back then and with a fresh coat of paint I'm ready to let the non pinned VT's rip and compare them to the NIO backend!
Netty has NIO... but back in 2000 there was no Netty. So 25 years back. JDK 1.4 with NIO came in 2002, Netty in 2004, but NIO only became usable live ~2010 with epoll and bugs got sorted end of 1.6!
Also Netty is one of those middleware "pretend to help but really bloat" libs.
I would sincerely recommend to avoid those as implementing on top of lower common denominators always is faster in the end after all the Netty bugs have been sorted.
i would prefer it, if they improve and extend the java standard library and the tooling for libraries
many people will tell you that the standard library is not as performant as it could be and does not have as many batteries as python and try managing your dependencies...
that would be far more important than the next super duper feature IMHO.
Scala definitely had some missteps but its much nicer to program in than Java and simpler as well. I mainly program in Go these days but I could imagine explaining the benefits of Scala over Go to my coworkers. It would be embarrassing trying to do the same with Java however.
Isn't there a textual version? Seems pretty inefficient to have a video that is basically a big list
Copied this from the video description:
JEP 395: Records: https://openjdk.org/jeps/395
JEP 440: Record Patterns: https://openjdk.org/jeps/440
JEP 394: Pattern Matching for Instanceof: https://openjdk.org/jeps/394
JEP 441: Pattern Matching for Switch: https://openjdk.org/jeps/441
JEP 409: Sealed Classes: https://openjdk.org/jeps/409
JEP 361: Switch Expressions: https://openjdk.org/jeps/361
JEP 456: Unnamed Variables & Patterns: https://openjdk.org/jeps/456
JEP 507: Primitive Types in Patterns, instanceof and switch (Third Preview): https://openjdk.org/jeps/507
JEP 512: Compact Source Files and Instance Main Methods: https://openjdk.org/jeps/512
JEP 458: Launch Multi-File Source-Code Programs: https://openjdk.org/jeps/458
JEP 511: Module Import Declarations: https://openjdk.org/jeps/511
JEP 502: Stable Values (Preview): https://openjdk.org/jeps/502
JEP 513: Flexible Constructor Bodies: https://openjdk.org/jeps/513
JEP 456: It’s crazy how glad I am to finally get rid of all those “ignored” vars in my code.
> try (var ignored = CloseableThreadContext.put(…)) {
to
> try (var _ = CloseableThreadContext.put(…)) {
After trying out many languages at many places, I reached the conclusion that Java, as weird as it may be, is my favorite language.
There were times I hated it, but turns out I really just hated messy, over-engineered legacy code and working in a gray cubicle at aging MegaCorps.
The language itself is quite beautiful when used properly and with modern features.
It just really needs a makeover and better tools.
it consistently works and has a huge ecosystem, but "beautiful" is never a word I would use to describe java. off the top of my head:
* no type level concept of a const object (ie, you can have a const reference to a List, but never a reference to a const list). this makes const-ness an implementation detail of the class itself! so frustrating that List:add() can throw depending on the underlying class.
* lack of tuples (and no, record doesn't count). this is just a syntactic sugar thing, but I really miss it from c++ and python.
* var is far less powerful than c++ auto.
in most cases, I actually prefer the syntax of c++, which is really saying something.
The missing constness is the biggest flaw of them all, in my opinion, especially when reading other people's code. Always having to dig deep to make sure an object isn't altered somewhere down the line gets old really quick.
> [Java] has a huge ecosystem
But does it though?
I've just taken a new job writing primarily Java whereas I was previously writing mainly python and typescript.
One of the first things I've noticed is how dead Java's ecosystem (Maven central) seems in comparison to other ecosystems like PyPI, NPM or Cargo.
(Also side note: I've published packages on each of these registries and the publishing process for Maven central is comically terrible! This has to be discouraging people from contributing to the ecosystem.)
probably depends on what kind of stuff you're working on. I mostly build web services and data pipelines on AWS, where java is still the best supported language (even if others have joined the list of officially recommended).
java might not see the same brisk pace in library development as other languages, but it's also 30 years old. aside from core issues with the language that can't be papered over by 3P libs, what's missing?
> The language itself is quite beautiful when used properly and with modern features.
I respect your opinion but I wouldn't call Java beautiful (of course it depends on your definition of beautiful). It takes so much ceremony to do things you would do without any thought in other languages .
Initiating a mutable set
Python
`a = {1,2}`
What most Java programmers do
``` var a = new HashSet<>(); a.add(1); a.add(2); ```
Shorter one but requires more ceremony, and hence knowledge of more language semantics
``` var a = new HashSet<>(new Arraylist<>(List.of(1,2)) ```
I don't know if the above works but the Idea is to initiate a list and pass it into a HashSet constructor.
Similarly Java 21 allows you to model union types but doing so requires you to define N+1 classes where N is the number of union cases whereas in other languages it's as simple as `type A = C |D`
`var a = Set.of(1, 2)` works just fine in Java.
Impressively simple - though there being three ways of doing something is a complexity in itself.
Perhaps I’m being too critical.
Not if only one is the right one in all cases.
In python you can also do
and But I wouldn't call that having three ways to do the same.Disclaimer: I don't know java well, just commenting based on the comments above and my python knowledge.
In python, those give a mutable set, which is what I was referring to above. Also not to put too a fine a point, you'll never see python code like that in the wild, but even in code reviews these days, it's common to find Java code written like in my example because the syntax for sets in Java came after Java 8
Thanks for specifying, as I said I'm not a java programmer. I stand corrected.
>it's common to find Java code written like in my example because the syntax for sets in Java came after Java 8
So I think we went full circle back to GP claim:
> The language itself is quite beautiful when used properly and with modern features
:)
My bad on the mutability. As far as code like that written in Java, I don't think I've ever seen something like that in anything non-toy and I started with Java 7.
I think that's fair. As with good UI, I appreciate when I can navigate via intuition. The solution in Python is intuitive, readable, and memorable. When working in Java, I frequently felt compelled to read and review pages of docs in search of a straightforward solution. I'm a sucker for Python's syntactic sugar.
I've never seen a language have a single way to do something. It's part of Python's mission statement, but it doesn't appear to even have been a consideration in practice.
I mentioned "mutable" in my comment
> The language itself is quite beautiful when used properly and with modern features.
> It just really needs a makeover and better tools.
I love java, wouldn't call it beautiful though. But I don't need a language to be beautiful, I need it to be pragmatic, blisteringly fast, have an extensive ecosystem and top quality tooling. Java delivers #1 on all of those so I love it.
For beauty, I like ruby and lisp, but those both fail on all the other criteria so they are mostly for hobby use for me. (Python, the darling of everyone these days, is pretty much dead last on every criteria except popularity.)
> better tools
I'd say Java & JVM has pretty much the best tooling on all fronts.
I can't think of anything that has better tooling around the language and runtime.
I moved to a C++/.Net/Python shop after working with Java, and I honestly miss Java.
I can't see why you'd pick Java over C#. IMO its basically all the same preferences with slightly more consistent syntax choices. That said, Java is great too. It's underrated considering what a workhorse it is.
First, C# has much smaller ecosystem. Half the stuff I was used to have for free and a very good quality was paid and much more limited in C#.
Second, the more I worked with C# and visual studio, the more I hated it. It was pretty much the opposite with Javascript, typescript and even Java.
Well the ecosystem is different than the language but you can certainly find 3+ of any library you need in Java where .NET usually has a single choice.
I use Jetbrains for both Java and C# and the experience is essentially the same.
C# is far more complex than Java. It’s a kitchen sink language.
Did you try Kotlin ?
I am a big functional programming geek. I am one of the few people on the planet who can honestly say I have been paid to write F#, Haskell, Clojure, and Erlang. I have spoken at FP conferences like six or seven times, and I have shit on Java for most of my career.
And yet, my latest talk at Lambda Days basically boiled down to “Java 21 and later don’t actually suck anymore”, and I genuinely do mean that.
Java 21 is actually fun to write, even for a grumpy FP advocate like me. Virtual threads make concurrency a lot simpler, and now that there’s proper records and ADTs (in the form of sealed interfaces), along with pattern matching, the language is actually pleasant to use. I haven’t dived into 25 yet, but I suspect I will like it as much or more than 21.
The biggest issue, though, is that Java programmers won’t use the new features. It was like pulling teeth at my last job to get people to use stuff from Java 8 (e.g. the `var` keyword), and none of my coworkers even knew how to use NIO or BlockingQueues which I think predate agriculture. I mean, hell, I had explain what “fairness” was to engineers when using a ReentrantLock because someone “corrected” my code with `synchronized`.
I don’t think Java makes people into bad programmers, but I do think it selection-biases for intellectually unambitious engineers. They learn exactly enough Java in college to pass their courses, and then get a job at a BigCo that doesn’t strictly require ever learning anything more than what they were taught in their “intro to data structures” course.
I have met some extremely intelligent Java engineers who do have intellectual curiosity, so I am not saying it affects everyone, but I do think that they are the minority. Java 25 might add every feature to make my wildest dream come true but it won’t matter if I am not allowed to use it.
> I don’t think Java makes people into bad programmers, but I do think it selection-biases for intellectually unambitious engineers. They learn exactly enough Java in college to pass their courses, and then get a job at a BigCo that doesn’t strictly require ever learning anything more than what they were taught in their “intro to data structures” course.
I think that's a fair comment, but also there's this perspective: I first touched Java 1.1 in 1997 in college, and only for a semester. Then for the next 22 years never looked at a line of Java, working mostly in C++ and Python plus dabbling in FORTRAN for high performance stuff that needed to be written there. I generally consider my self not intellectually unambitious.
Then I moved to a Java shop who specifically needed high performance math (well at least as high performance as you can get in Java, which is actually pretty good now). But we stick to Java 8 compatibility because we have some BIG customers who require that because of LTS on Java 8. There are some nice constructs that would help make the code more readable and modern, but when you need to support people and actually make money you do what you need to.
Sure, I am not claiming that you have to use every new feature every day 100% of the time. Obviously there are cases where you can’t upgrade for legal or compliance or “customer is just being difficult” reasons.
A lot of Java jobs aren’t that though, especially internal applications. A lot of places are running Java 17 or Java 21 on all their servers, literally have no plans to ever support anything lower, but the engineers are still writing Java like it’s 2003. That is what’s maddening to me.
I think it has a lot to do with work culture. Many tend to mimic what others are doing in order to not stick out.
At my previous job some were able to change that by consistently using "modern" features of Java. It inspired others to change and eventually we ended up with a good code base.
Be the one to start the change by implementing new features using good code. This will give others "permission" to do the same. Also try to give soft suggestions in code reviews or pair programming of simpler ways to do it (don't push too hard)
At my current job all of us were eager to try the latest features from the start, so we never had to convince new hires.
That's how it kind of was in my last job [1].
I know I came off as a bit negative, but in fairness to them, they did more or less continue working on what I was doing using the newer Java 21 features, and after I got a few pretty interesting changes merged in some of the more junior engineers started using them too; particularly I was able to successfully evangelize against the use of `synchronized` in most cases [2] and got at least some people using queues to synchronize between threads.
It honestly has gotten a fair bit easier for me since I've been doing this for awhile; at my last job I was the most experienced person on my direct team (including my manager) and one of the more experienced software people at the company, so I was able to throw my weight around a bit more and do stuff how I wanted. I tried not to be a complete jerk about it; there were plenty of times people would push back on what I was doing and I would think about it and agree that they were probably right, but I outwardly rejected arguments that seemed to be based on "I didn't learn this in university so it's wrong".
I have had other jobs (at much bigger companies) where they were not amenable to this. I would try and use new features and my PRs would be rejected as a result, usually with some vague wording of "this way is faster", which I later found out was (as far as I can tell) always a lie.
[1] It is not hard to find my job history but I politely ask you do not post it here.
[2] I'm sure someone here can give me a contrived example of where `synchronized` makes sense but if you need mutexes I think you're almost always better off with a ReadWriteLock or ReentrantLock.
Programming language is a tool. Java developers value stability and ease of understanding for the code. I've seen the nice features you complain that don't get used, reality is that nobody wants to waste time knowing them unless they are intuitive to use. Especially when are forcing to use newer JDKs.
There is no value in solving a challenge in a way that only you understand or make others lose time trying to understand the logic.
Java 8 was the peak of development age for the JDK. Everything that came after isn't really memorable nor helpful, especially lambdas. You mention "var", why would we ever want in Java to hold a variable that you can't read immediately what is the type? That is a source of bugs and time waste to track down what object is being used.
I don't mind you are happy with all these changes, just remember that we absolutely don't care about them nor will make much of an effort because in the end of the day we don't want to follow the same route of other programming languages unable to handle gigantic and complex platform systems.
This isn't a competition to showoff who can apply new tricks, we absolutely don't care about functional programming. Java code must be simple and easy for anyone to read, that's it.
The new features make the code easier to read. For example var:
It just reduces visual noise and boilerplate that you already know.Java 8 is also a slow and old runtime. It performs terribly in 2025. Here’s a quote from 2020 and the gap has only gotten wider [0]:
> JDK 8 is an antiquated runtime. The default Parallel collector enters huge Full GC pauses and the G1, although having less frequent Full GCs, is stuck in an old version that uses just one thread to perform it, resulting in even longer pauses. Even on a moderate heap of 12 GB, the pauses were exceeding 20 seconds for Parallel and a full minute for G1. The ConcurrentMarkSweep collector is strictly worse than G1 in all scenarios, and its failure mode are multi-minute Full GC pauses.
You’re doing a disservice to everyone by continuing to use and glorify it.
[0] https://hazelcast.com/blog/performance-of-modern-java-on-dat...
Using Java 8 doesn't mean using the java 8 JDK. It just means not using the newer features. I don't see anything in the parents post about restricting which JDK they use.
> Java 8 was the peak of development age for the JDK.
Still glorifying it.
There is about zero difference in readability of your two statements, rending var not useful.
And 'var accounts = calculateAccounts(something)' is literally less readable, because now you dont see what exactly accounts is.
Var statement speed up writing, then are either irrelevant or gets rewritten to types for better readability.
the advantage of var is not clear in toy examples. in real life, people write stuff like
literally anything to reduce the number of line breaks needed for a single statement is welcome.I agree, and I'm not sure how something like 'var accounts = calculateAccounts(something)' can be thought of as better in a code review setting. I suspect using "var" or equivalent will be considered a problem by most companies within the next few years.
This experiment has already been run with pretty much every language that isn’t Java or C, and I think most people have been ok.
Yes, code review is arguably worse, but most people who write Java do so with an IDE and the type is never unknown at that point.
Also, if your function is big enough to where the type of var is not very clear, it likely shouldn’t be passing code review regardless.
Those writing in python and javascript don't honestly know any better. They grew in a world without unit testing or without products that need to grow into gigantic systems maintained over the next decades.
This reminds me of a developer writing in jRuby because "it was better". While he was in the company he'd still give support to his own works, after leaving nobody else wanted to pickup those "better" things and would prefer to write workarounds to the tool, it effectively became a black box that few could improve and even worse to test. As result, those portions had to be rewritten in proper Java so that we'd be able to deeply measure/test and improve.
Never again.
There’s nuance in programming. Both of these statements can be true. Var reduces boiler plate when you’re duplicating information in both the lvalue and rvalue.
I’m in agreement that when the information isn’t in the rvalue that you shouldn’t use var.
Var can help in maintenance. Change the return type of calculateAccounts and you don't have to modify this code (assuming that it duck-types out equivalently).
That isn't necessarily a huge win: it does force a compile change that isn't obvious from the source. And a refactoring tool could have performed the code change automatically so it's not as big a deal as it might have been.
I would argue that it helps me when performing maintenance to see and correct where types may have changed. Not always, and sometimes it is busy work I agree, but overall I prefer it.
Yeah I generally avoid using 'var' to elide method return types for that reason. In my early phase of var enthusiasm I even had it result in a bug that would have been caught if the type had been made explicit.
I do still really like it in the `var list = new ArrayList<String>()` case though.
Modify it when? If the interface renames? IDE does that. If the actual return type changes? They you do have to check that place whether it is still valid.
Maintenance involves a lot of reading of unknown code. That is literally the situation where you are rewiting var to specific types to figure out what is going on.
I tend to agree here, I prefer explicitness. Even though yes it does mean some pain when refactoring, it also creates very clear diffs showing impact, which I consider a positive.
Ideally this could all be dealt with via tools. An IDE that shows whatever the user prefers, but is actually saving into a format defined for the repo.
It is less readable because either accounts is named incorrectly or calculateAccounts is.
If it should be createAccounts, nothing is lost unless you have multiple account types in which case you would call it createUserAccounts, create adminAccounts etc.
> is literally less readable
Please point me to an objective measure of "readability" that holds for all people. And then demonstrate that your example is "literally" lower on that scale.
Readability was a term coined by people writing languages that are litterally unusable without an IDE to hide stuff when they render the source file to explain why refusing to learn languages with curly braces is sane.
Oh man.. that comment really hit deep. I'll never understand why people argue that it is "easier" to adopt invisible characters and exact tab positioning instead of just using braces or similar to mark the code blocks.
Drives me nuts because you really can only write code for those programs with an IDE that is prepared for the case. Try to edit the code directly from the github web interface and it won't compile.
Whoops, my initial intent was "without curly braces", since that's where most of the readability comments are from. I guess the comment works both ways, though.
> Drives me nuts because you really can only write code for those programs with an IDE that is prepared for the case.
Usually people have strict rules for that (e.g. pep8), and most text editors will do it for you with little configuration.
> var accounts = calculateAccounts(something)'
Where did I write this? It’s almost as if there’s nuance in programming.
Var helps when you’re duplicating information that is already known. Your example is clearly not that.
> You mention "var", why would we ever want in Java to hold a variable that you can't read immediately what is the type?
var items = new HashMap();
Instead of
HashMap items = new HashMap();
That's the point of var. It reduces noise a lot in some situations.
You should write
It allows you to limit `items` usage to `Map` supertype and allows you to swap implementation easily, if necessary. `var` is weird feature, because it allows people to use implementation-specific methods methods and tie code to a single implementation, essentially making it less agile.There are valid use-cases for `var`, but IMO this feature should not have been added to the language, as it's too dangerous and most people won't use it responsibly.
Your example isn’t good though; var is only for local variables, which should be perfectly allowed to use implementation-specific methods.
For argument types you don’t have var, so methods that take in a map can stay abstract and you can still pass in the implementation-specific version into those without casting it to the interface.
ETA:
I guess I am trying to say that if you want to be abstract, they should be at the argument or properties level. Local variables should be used locally. I agree that generally speaking you should try and prefer using Map for anything shared across different parts of code but I am not convinced it’s bad to have var be implementation-specific; if your method is big enough to where swapping out implementations like this will take a lot of time, your method is probably too big anyway.
No, you shouldn't write that. Your variable should represent the complete implementation. Where you use it should be as generic as possible, for example, the parameter in a function call.
Why would you want that? The whole point of an interface or superclass is to let you swap implementations without changing everything.
I also like the nudge it gives you to use only the higher level methods, rather than the ones specific to the subclass unless they're needed. That also improves flexibility.
It’s not about swapping implementations, like the original poster suggested, so that you can later come in and swap out different implementations in a local sense. Interfaces are used to decouple parts of an application; for example mocking test interfaces. Another way to look at it is that interfaces are a contract that needs to be fulfilled.
Declaring a local variable as an interface to hide functionality so you can swap out functionality later is misunderstanding the fundamental theories around interfaces. Your variable should be whatever the function or method returns. If you want to abstract the type so it can be swapped out, create a function that returns an abstract type. Don’t tell the HashMap constructor it did the wrong thing. Now that I think about it, I’d recommend you use “var” in all cases, and not try to redefine the return values from a function.
While I do use `var` when appropriate, your example is in general not appropriate
You would like to use Map<Key, Value> items = new HashMap<>() since in general you do not want implementation detail leaking into contracts
Using var there doesn’t leak any implementation details into any contracts. Var is for local variables inside of functions. There are no details or contracts there.
> why would we ever want in Java to hold a variable that you can't read immediately what is the type
I can use my IDE to see the type if necessary.
> Everything that came after isn't really memorable nor helpful,
There are several improvements that are very helpful
One example is how multi line strings help me to read more clearly without the unnecessary string concatenations:
Another example is how switch statements have improved code readability, at least from my personal subjective viewpoint.You could have as easily used String instead of var and there would be no ambiguity. This gets worse for numbers where it is important to know if you are dealing with an integer, long or something even bigger.
I agree with you on switches and do like the """ feature, thought. It was a real pain in the rear to include the multiple + "\n" back in the old days. This is a very clean and intuitive improvement.
> You mention "var", why would we ever want in Java to hold a variable that you can't read immediately what is the type?
I've been a full time java developer for the past 7 years. Let me start by agreeing that I have very little interests in the functional "innovations" they added. They're fine, but most of my colleagues agree that code using streams or lambdas very quickly becomes harder to debug then if you just wrote the code with loops and.
That's far from true for every java feature though. Switch statements have been super cool, Green threads are a promising road out of the CompletionStage hell the children are dreaming of these days. "var" is a very nice way to reduce double typing ("Thing x = new Thing()" -> "var x = new Thing()") and also a nice way to avoid changes to unrelated files ("Thing x = getFoo(); f(x);" -> "var x = getFoo(); f(x)" means changing the name of class Thing doesn't require any change to the code) that's been helpful in a lot of cases.
Yes, please don't get me wrong. There are always a few good things, but like you mention with streams: the person writing it can have an idea of what he was doing but the others afterwards will struggle to make changes and will likely revert the code base to simpler code.
Switch statements got better. I'm also getting used to Paths albeit don't yet understand why File wasn't already good enough. It is difficult to find compelling reasons for upgrading the JDK since I know it will be hassle for everyone that later down the line has to install/run the product.
If I had to guess, I'd say that a vastly bigger problem in the world is legacy code written in decades-old obsolete and inconvenient language dialects than code that's "too new".
I do not want to be rude but I am going to be: this entire comment illustrates my point very well. Instead of trying to actually learn new stuff, you say that the language peaked thirteen years ago.
> Everything that came after isn't really memorable nor helpful, especially lambdas.
Lambdas came out in Java 8, along with the streams API, and the fact that you don’t think they’re useful more demonstrates to me that you don’t actually understand it, since nearly every language before and after Java has lambdas and nearly everyone agrees that they’re useful.
Reifying a bunch of temporary interfaces is not “more readable” than a lambda. A bunch of terrible nested for-loop logic is not “more readable” than the streams API.
> You mention "var", why would we ever want in Java to hold a variable that you can't read immediately what is the type?
Your IDE can show the type, but even disregarding that there are lots of cases where you have to write the type twice in Java and it just makes noise. It doesn’t make the code more readable.
> we don't want to follow the same route of other programming languages unable to handle gigantic and complex platform systems.
Burying your head in the sand and Ignoring improvements in the language doesn’t make you more able to handle complex problems. It actually does the opposite and that’s so plainly obvious that I don’t think you actually thought through the sentence before you wrote it.
Take something like virtual threads. Most Java programmers don’t use them and instead keep using an executor service incorrectly because they also never learned the difference between blocking and non blocking IO. For them, virtual threads would be strictly better because it properly parks blocking IO.
Ultimately, I guess I disrespectfully disagree that Java “peaked” in 2012.
You know what's quite more important?
* Performant and safe standard library. * batteries included * a good way to actually care about managing dependencies, during build and runtime.
Okay, you got your stuff, please everyone now let's care about the standard library and that it really good.
> Especially when are forcing to use newer JDKs.
Dude, java 8's eol was 8 6 years ago, now. I have nothing gainst waiting a bit for "newer JDKs", but way too often the pattern is that teams use the oldest possible JDK and only migrate several months/years after the last possible vendor has sunset their support.
> Java 8 was the peak of development age for the JDK.
To me it looks it was merely the point where your stopped caring.
I view this largely as a symptom of the widescale “success” of the bloated J2EE app servers in the early 2000s to mid 2010s. Your Java version and dependencies were locked in and upgrading was a massive effort. A large group of developers stagnated on Java 1.4.2 and 5 and seemingly never updated their use of the language, even when moving to Java 8 and beyond. The legacy stuff keeps ticking along.
I kind of think thats backwards. I think the success of the abomination that we have decided to label as J2EE or Jakarta was attractive to intellectually unambitious Java engineers because it progressed so slowly.
I think people here are really underestimating how intellectually lazy most people are at their jobs. HN selection-biased for a geekier crowd so a lot of my criticisms don’t apply to readers of this forum.
Sorry, but I don't think a preference for slow evolution is always because of laziness. What's wrong with wanting to keep improving on a skill instead of having to waste time relearning things every six months?
Software is rare among arts/crafts/whatever in that it is difficult to find nice areas of software to keep digging deeper into (as curious people do!) rather than having to move on to something new just when you start to be good something.
It's not even wanting to focus on depth instead of breadth, as the constant changes means you are barely able to keep using your older skills, so there is little actual breadth more like constantly moving between shallow pools of knowledge. Maybe it feels great to constantly be moving, but I do not see how it is productive or positive in any way for us.
I am arguing that they aren’t improving their skills in any regard, including how to properly use the tools they already pretend to know. They don’t go “deep” or “broad”, they don’t learn anything more than what they were taught in university.
I have had to debug a lot of concurrent Java, so my opinions are skewed towards that, but I have seen cases where “staff engineers” label every single function as `synchronized`, who genuinely don’t know why you would use a queue, have no concept of thread starvation or fairness, when to use an atomic instead of a mutex, and genuinely do not seem to understand that for network applications it’s generally more important to figure out how to reduce latency than trying to choose the optimal hashmap or sort implementation. These aren’t arcane details that require a PhD in category theory or require being constantly plugged into hacker news, these are extremely basic things that these Java engineers do not know.
I think most Java engineers, like more professionals in general, are very bad at their jobs. I think Java just selection-biases higher than other languages for people who are bad at their jobs.
Yeah, people often underestimate the importance of community when they discuss programming language. In fairness to the Java crowd, you rarely see codebases looking like the team was trying to fit every new concept from some blog they read somewhere in the codebase, like you do in e.g. Scala or Haskell.
> The biggest issue, though, is that Java programmers won’t use the new features.
My old CTO boss swore he wouldn't ever use annotations because they were too much magic for him.
"No! Writing out gobs of XML to configure Spring DI is the only way!"
I generally hate when people use the “magic” excuse for not doing things. Most of these tools are open source and/or can be readily viewed in IntelliJ. It really isn’t hard…cmd+click on what you want to look at.
If you’re an engineer you should be able to easily read the code to see what’s going on. Most of the time the “magic” can be understood in less than 30 minutes and then it’s not magic anymore.
You're right, "magic" is generally when someone doesn't take the time to understand things. A busy CTO may not have time, so more things seem like magic to them.
What's inexcusable is to inflict the rest of your team with your nonsense.
Yep, completely agree.
I got in a bit of a disagreement with teammates at a previous job. I liked these teammates, they were very smart and nice people, but for a specific project I wanted to use LMAX Disruptor, and the excuse for not using it was very literally “we don’t want to make people learn anything”.
That stuff blows my mind; aren’t we engineers? If we’re not willing to learn new things and adapt, what exactly are we offering that a high school kid who bought an “Intro to Java” and “Intro to Spring” book can’t?
I wouldn't dare say XML is better in this regard, but a good reason to be conservative with the use of annotations is exactly that cmd+clicking them doesn't easily lead to where the behavior is implemented.
Yeah, fair enough, though it's still not too hard to find where the behavior is implemented if you have access to the source code.
But you're right, cmd+clicking on the annotation just shows where the annotation is defined, not where the behavior is implemented.
Sure. It is just another keyboard command in IDEA.
I don’t use Java often, but many developers at my workplace do; they universally hate the new FP features.
I thankfully have not had to use Java for years, but at one of my first jobs after grad school I was hired because I had Kotlin experience and they were moving the code base away from Java.
A few of the older developers also complained about the use of map, filter, zip, lambdas, etc being harder to read as well. Then a month or two later when they realized they weren't going away, it was an important part of the language, and just learned how to use them the complaints just one day stopped.
Except for when we had to touch Java code and it didn't make sense to convert it fully to Kotlin.
And then there are those of us who have used Java for a long time who feel the FP features make Java tolerable.
This is precisely my point. For a lot of Java programmers anything that they didn’t learn in university is “bad”, as far as I can tell, purely because they don’t want to actually learn new things.
They’ll give half-hearted justifications that are usually reductive or just flat out lies [1], but ultimately it seems to boil down to new=bad.
The amount of terrible code I have had to debug because Java programmers haven’t figured out you can use queues is upsetting, because all they learned in university is how to use `synchronized` wrong.
[1] I have learned that nearly every time someone says their disgusting code is “faster”, even when they claimed they tested it, it is almost universally untrue when I write a microbenchmark to check it.
Everyone seems to love them at my workplace and in /r/java.
That's called selection bias for both of you. Reddit will expel anyone whose opinion does not correspond to the local mob bias. Workplaces select people who are similar to already working ones.
I’m well aware. I can still offer the contrary point of view.
This is amazing news! Two questions for you:
- how’s the environment? Build tools, dependency management, etc. it used to be a PitA back then. - how has the typing system and generics evolved to support this? Have they introduced any type of variance?
Maven still sucks.
IntelliJ IDEA is genuinely great. It helps that they were the ones who developed Kotlin, and a fair bit of the actual language changes were gacked from Kotlin. (Or you could say "prototyped and shown valuable in Kotlin".)
They are still hampered by lousy nullable support.
You didn't ask, but Spring still sucks. It's not part of the language but it's a ubiquitous framework.
It’s amazing that Java has access to really great, high performing web toolkits like Vert.x and even Play, and yet the entire enterprise Java world has converged on Jakarta and Spring. I don’t like Spring but in my mind there is a special place in hell for Jakarta/JavaEE.
What’s wrong with Jakarta? (Genuinely curious)
[dead]
Generics still kind of suck but they’re workable enough. As far as I am aware they’re broadly unchanged and I don’t think they added any kind of new variance.
It turns out, though, it’s still good enough for sealed interfaces and the like; I don’t have too many issues with it, though that might just be Stockholm syndrome at this point.
Maven is terrible as always but Gradle is generally fine. I use IntelliJ community edition solely for Java and it works well enough for what I need.
It’s not like Java is going to replace F# for me or anything, but I do genuinely think it’s more fun to write now than it used to be.
Maven is generally fine. The only complaint anyone has is “xml bad”. Outside of that maven is probably (?) better than gradle. At least it prevents a custom scripting mess.
What do you think of “modern” Javascript to write FP?
I haven’t touched much modern JS. I have done some TypeScript in the last few years but of course that’s a transpiled thing. I think TS isn’t too bad to write functional stuff though I have no idea if people actually enjoy reading my code.
var was introduced in Java 10 not 8.
Yep, just looked it up and you are right! I misremembered. I stand by my overall point though.
The explanation on why Java lets you use unnamed variables to prevent accidental use during deconstruction, but those unusuable variables still getting initialized by calling accessors, is a perfect example why I dislike the way Java implements improvements.
Every single, logical step that led to this hidden performance problem makes complete sense, because every improvement had to be its own tiny, contained improvement, but the end result is that `case Foo(int _, String _, User _, int age)` will still call three getters _just in case_ you're abusing the language to add side effects to accessors.
Perhaps even worse is the explanation that follows: if you don't mess up your accessors, the JVM _may_ decide to not call those accessors at runtime. So now the language itself has this weird performance impact to maintain backwards compatibility, but at runtime that backwards compatibility _may_ not exist and provide you with a performance improvement instead, negating the whole reason why backwards compatibility was added in the first place.
I like the improvements to Java, don't get me wrong. It's no longer the JDK 1.7 language poor enterprise programmers are stuck with. But if the Java people had come together and worked this out as one single feature, rather than five different ones, we wouldn't have needed to remember edge cases/a code analysis tool to remind us that using this intuitive language feature _may_ actually has a 3x performance impact depending on the mood the JVM is in today.
I’ve worked in Java for over 20 years. Being the “lingua franca” of the enterprise is its biggest strength IMO, but it is also perplexing to me that it was able to do that in the first place. The language itself is not bad. The tooling around it is very good. But the codebases you encounter written in it, particularly in the enterprise, are often horrible.
Mind sharing why do you consider Java tooling to be good (and largely, what is good here)?
The reason I ask is that I recently had to join a Java project at my company, and having a background in Node/Rust/Perl/Lua and some C++, I found the Java tooling to be extremely unsuitable for my taste.
A simple example: there is no standard LSP server, and the amount of jumps required to have a working setup with FOSS tools and make it IDE-independent is just horrendous. In every other ecosystem I've worked with so far, it was pretty easy in the last 5 years: if you don't like IDEs, you can keep using your vim/emacs/helix or whatever and just embed a plugin or two, with LSP integrated -- and you're ready to go.
Java world felt complete the opposite, like you had to use/buy some commercial tools to start doing something.
Tooling goes way beyond the editor/IDE. Eclipse is a very good free option. As is IntelliJ CE. I personally have the all products pack and use the ultimate version.
Beyond the IDE you also have to consider the build tools, package management, debuggers, profilers, static analysis tools, etc.
It’s honestly too much for an HN comment. But as an example, if I do open one of these awful projects at work and it uses gradle for example, intellij will understand that, import the project, get all dependencies, let me run any target with debugging or profiling, give me code coverage, etc.
Thanks for sharing your experience.
It's a fair note about tooling in general, I started with the code editing because it's the first thing before you can taste and judge the rest.
I think my frustration comes from the fact that in most other ecosystem I can use the tools I like, but in Java I have to use things like Intellij.
Intellij CE may be open source, but it is entirely owned by a private business whose primary goal is to sell their product - which affects how well are the integrated, open to accept community feedback, etc.
There are several open source tools for Java (Eclipse, Visual Studio plugins, Netbeans and others).
The reason I don't use them is not because they are bad, but because IntelliJ is so much better.
I even use IntelliJ Ultimate for non Java code like React, even though Visual Studio Code seems to be de-facto standard for React developers and guides.
I guess it's just a given that everyone uses IntelliJ Ultimate. If you don't, I guess you're right, the tooling is lacking outside of that. But it's so worth it...
The eclipse language server works perfectly fine in vim/emacs/vscode/editor-of-choice.
> But the codebases you encounter written in it, particularly in the enterprise, are often horrible.
Not sure they are worse than other languages?
Having now experienced such Java codebases for the first time in my 15 year career… no, the Java code is much, much worse. Over engineered and otherwise poorly written Java code is SO much worse than other over engineered code I have worked with.
Honestly for the limited projects I've had to work with... I can attest to that. It's the over engineered abstractions that get me. Traditional Java EE users use WAY too many abstractions and interfaces, it's absolutely horrible to debug.
The only new feature I might use since 1.8 (that only had NIO stability and GC improvements over 1.7) are the virtual threads without pinning in 24.
We extensively use vars, switch expressions, collector methods, String format methods, records, instanceof narrowing. Just from the top of my head.
Same here, plus sealed classes - a very powerful feature.
What I actually haven’t used up to this point are VTs. I got a service that implements a job queue and it currently works flawlessly with scheduled executor pool. I’m reluctant to go with VTs before evaluating what implications that may have.
I think VT's are only going to be interesting if you need 1000s of them doing tiny amount of small work often.
For me I have been waiting for them for 20 years, as the blocking IO made MMO development impossible.
In the meantime we got NIO in 1.4 that only became performant and stable around 1.7 so it's been a slow progress.
I dug out my old blocking server code from back then and with a fresh coat of paint I'm ready to let the non pinned VT's rip and compare them to the NIO backend!
> For me I have been waiting for them for 20 years, as the blocking IO made MMO development impossible.
I'm not into this area of work, but wouldn’t Netty have been helpful even before VTs became available?
Netty has NIO... but back in 2000 there was no Netty. So 25 years back. JDK 1.4 with NIO came in 2002, Netty in 2004, but NIO only became usable live ~2010 with epoll and bugs got sorted end of 1.6!
Also Netty is one of those middleware "pretend to help but really bloat" libs.
I would sincerely recommend to avoid those as implementing on top of lower common denominators always is faster in the end after all the Netty bugs have been sorted.
i would prefer it, if they improve and extend the java standard library and the tooling for libraries
many people will tell you that the standard library is not as performant as it could be and does not have as many batteries as python and try managing your dependencies...
that would be far more important than the next super duper feature IMHO.
> many people will tell you that the standard library is not as performant
such as?
Java is becoming a poorly implemented Scala.
Scala is a dead and complex language.
Scala definitely had some missteps but its much nicer to program in than Java and simpler as well. I mainly program in Go these days but I could imagine explaining the benefits of Scala over Go to my coworkers. It would be embarrassing trying to do the same with Java however.
I don’t think anyone I know would classify Scala as simpler.
In my opinion Java is a much better language than Go. I can’t imagine anyone liking Scala would prefer it over Java.
Having coded in Java for 15 years and Go for five, I can say that Go is clearly the better language for most tasks.
That's just like your opinion man. Mine is that its far too verbose and error prone.
any reasons to use Java over Typescript, Go or Rust for server side programming?
Spring is very capable and has lots of things built-in
[video]
TLDR: