Everything Bad is Good for You is a pop culture book that points out that some things we assume are bad (like TV) have tremendous benefits to our w...
For further actions, you may consider blocking this person and/or reporting abuse
I work with Java from time to time when I create extensions for a Neo4j database. It is much more fun now, than it was ten years ago. I stumbled upon the same issues with exceptions and null values in the context of FP.
What I'd like to see in Java are simple JSON-like object and array types like
var obj = { name: "Joe" }
. I think in Java they exist as Record types, but still need to be defined statically in a separate file. I think FP-style functionsfilter
,map
,reduce
in combination with these object/array literals would enable much nicer programming in Java.From Java 14 you can indeed use records (Source
And then us it easily:
Hooking in your Functional Programming (I think you meant), you can already do things like:
It has some ways to go, but everything is extensible anyways.
That looks actually better than I expected. Thank you for this example!
It would be even nicer if this were possible:
Just declaring
{ name: "Joe" }
ornew { name: "Joe" }
(like C#) should be enough to define a record.These are called Tuples and Manifold supports them!
See my recent series covering it.
But yes @nlxdodge is right that records are pretty great.
Cool, I didn't know that Java supports extending the language with features via compiler plugins.
Your wonderful article surely needs a proper response, damn me and my tight schedule.
Talking from experience here, I'd say this is lovely to have a bit more narrative on the ...most odd()* parts of the Java language and ecosystem, and what led them in that direction.
It come to me a little enlightening why these trade-off came short in Java world as soon as the .
Picking from your Failure section I can picture two different context, just by comparison.
In the first one you got a very self reliant application, able to determine in full all the data structures, where the developers are in total control of how these changes and they own it in full. Failing fast in this context is gold, and
NullPointerException
is a way of handling that, possibly catching up all the errors during development.In the second context you're interfacing with very unstable remote API and highly mutable or versioned data you want to treat but not really own.
Developers main concern here is having a resilient application, able to keep functioning and resisting minor structural changes and flexible data, with uncontrollable
null
values.This means that
Optional<>
becomes king, and aNullPointerException
doesn't really help because they'll pop out randomly at runtime, even during perfectly valid requests.This is just to say that Java had a way to become tolerant out of its own "best use case scenario", even when the context requires a lot of stretch.
Your take on checked exceptions contradicts the one on nulls, if checked exceptions are good, why checked nulls are bad?
Also, you are wrong about kotlin, there is no duplication between the nullable and non nullable references, the difference is that you are forced to check for nullability and, after that point, the compiler understands that the value cannot be null.
Developers can make decisions that are not known to be bad until it's too late, and java is not free of that
Great point.
But no. Imagine the NPE was a checked exception... The checked exceptions should be used for very specific things where we MUST do a cleanup or have well defined failures. E.g. database connection, IO, etc. These will fail and we need to write code for failure. A null is something we can address, once we do that it will never fail.
When I said duplication in Kotlin I meant in terms of syntax and mental capacity. We need to make a decision early on when writing Kotlin code about the nullability status and if it changes then we either need to start adding null checks in the "wrong place" or start going back and change a whole chain of calls.
It's 100% true that Java is not free of bad decisions. The part that peeves me is that pretty much every discussion of nullability presents it as a 100% bad feature and repeats the same broken arguments. This is a tradeoff with advantages that go both ways.
Look at what Rust did to nullability, there is no null is Rust (at least in safe Rust), you have to use the
Option
type and deal with the presence or absence of the value. Under the hood the compiler optimizes everything and aNone
uses the same memory as a null in C.And in Kotlin there is not so much in terms of mental capacity of the syntax, if you want a reference to be nullable, you mark it as nullable and the compiler forces you to check it before using it:
And let's not forget some features like
?.
and?:
to make the code less verbose.Kotlin will also validate return types, you cannot return null nor a nullable if the return type is not marked as such, this will also prevent NPEs so you do not fail at runtime (possibly in production).
I had to deal with projects that failed with NPEs in production, and that was no fun at all. It was only in Java 14 that the NPE message pointed out what as null. Nullability checks at compile time would have helped prevented this. Failing at compile time is failing fast enough?
I do not hate Java, nowadays I kind of prefer Java over Koltin since Kotlin tries to be too much clever and this can make your code unreadable if you are not careful, but we need to be fair with its strenghts.
Rust is great but it's super complex to get right. It's also sometimes hard to map C based APIs to Rust in part because of its very different approach to everything. The alternative to Rusts approach is manual allocations or reference counting. Both suck.
What I'm saying about Kotlin is that it forces a location where nullability is deliberated even if it doesn't matter. Most developers implicitly choose non-null and end up creating more elaborate code since an API now requires a value even if it will never be used. There's a vilification of null that isn't justified.
Sure. I got a few of these but not as much is recent years since we learned to do CI properly and integrated tools like Sonar Qube. IntelliJ/IDEA also highlights most potential failures well before a commit. The null pointers I saw in production over the past decade or so, wouldn't have shown in compile time.
I find your article refreshing and agree with it.
One problem with a lot of languages today is the idea that they all have to have all sorts of features to "keep up". When in fact, they each support different programming styles.
The only thing that annoys me about Oracle Java is that they come out with a new version like every week and you can't easily tell if version 17 is one I need to pay attention to or should I wait till version 20.
In the Sun days you could tell because they had major and minor versions. So you knew that 1.3 was a big deal compared to 1.2.2.
Yes this is hard to keep track of. I suggest keeping up only with LTS releases which makes this more manageable. Specifically 11, 17 and the upcoming 21.
You're trying to address the boilerplate issues in Java and the only thing you can come up with is the semicolon?! Really?!?! If you want to address the boilerplate and other bad things with Java, just take a Kotlin vs Java comparison page and see what Java could have been.
Do that. Please. Most of these aren't applicable for current Java assuming you ignore standalone functions. Especially if you use Lombok or Manifold.
If by "current" you mean version > 18, then I'm afraid you are out of touch with reality and I will refer you to this meme:
reddit.com/r/ProgrammerHumor/comme...
And this video:
youtube.com/watch?v=Ibjm2KHfymo
Java is used mostly in corporate environments where the Oracle lobbyists are doing a great job pushing outdated products like Weblogic, running on Java 8.
What's your complaint? That they can't go back in time and update Java 8?
That is an unfair standard that you don't apply to any other language, platform or runtime. Python 2.7 shipped for years with Mac OS. Installing or updating python on a Mac was a nightmare of conflicts and failures.
The reason this is more common with Java is due to its success in the conservative enterprise environment where none of its competitors have a foothold. Most of these environments don't even use containers yet which is part of the problem. With migration to containers using the latest and greatest becomes much easier.
Also both Manifold and Lombok work just fine with Java 8 which is already the minimal version for most installations.
My complaint is that you wrote an article to make a point, that all bad things in Java are actually good for developers, yet you're not presenting the bad things that the vast majority of developers have to deal with in practice. You didn't specify which version of Java you're referring to, and judging from the article, I assume it covers all bad things from at least version 8 and onwards. Now, if "current" Java (21?) has no bad things, that's great, you can write an article and try to convince people to use Java 21 over e.g. Kotlin or Scala or any other JVM language. But if you really want to address the bad things that Java is taking the heat for, and you mention boilerplate/verbosity, you can't just write about the semicolon, as if there's nothing else verbose about the language. You wrote:
"This used to be a bigger issue in the past but looking at a typical Java file vs. TypeScript or JavaScript the difference isn’t as big."
What's a "typical" Java file for you? A typical Java file for me doesn't come even close to TypeScript/JavaScript in terms of verbosity and boilerplate. In fact whenever I see Java developers write JavaScript/TypeScript code, they repeat the same mistakes that they learned writing Java, because they cannot think in any other way. The code I get to see every day working in a corporate environment is just terrible. At this point, if we were to migrate to Java 21 to enjoy the good things that you're talking about, we might as well ditch all Oracle products and switch to a more modern (and free) stack altogether. But this is not going to happen, so we're stuck with the bad things. And this will be my last comment here.
Most of the things I discuss still apply. Even with Java 8 the only difference is verbosity and even that isn't as bad especially with Manifold or Lombok.
Your complaint is against enterprise deployment policies, not against Java.
Feel free to provide an example. There are some places where Java is at an inherent disadvantage due to its static nature. But tools like Manifold make things like JSON parsing as easy in Java as they are in JavaScript.
Another bad thing in Java: Missing "unsigned" int/short/long/byte types...
As a guy that does a lot of low level system programming I'm a bit conflicted about that. But no, I don't think it was a mistake. Unsigned is one of those things that makes sense in some edge cases. Since we don't have structs and unions that let us map things directly in memory, the layout of the memory doesn't matter. So we can't do some of the tricks we do in C when working with unsigned values.
Without all of these then unsigned becomes just another bit which seems like a bit much when we have a
long
andBigInteger
.The code you have to write without those types is much uglier and harder to read. I'm also doing system level stuff and image operations. It does simple makes no sense to handle a few million pixels with BigInteger objects. So you have to expand to long via extra instructions and/or doing some brain knots to get what you want.... From my point of view it was a big mistake. I'm doing some instrument controlling actually in C#, that's much easier, even if I doing Java since version 1 :-(.
Don't hit me, but I also like the out/ref parameter sometimes and the $"{value}" string construction (the new java construct for that is also much uglier).
I don't like, if the language force you to use stupid tricks to reach your goal.
Yes. I usually go into C to do that sort of stuff but I do have plenty of code that does the byte cast or
& 0xff
nonsense. I think it's Java focusing at what Java is good at. It's also probably part of the reason why C# is doing so well in the gaming industry compared to Java. Doing low level graphics in Java always included some hassle.If you like the
${value}
syntax check out my recent series on manifold, you can use that syntax in Java with it.Cool :-) Many thanks for that hint.
This point is not true. Spring started to use unchecked exceptions before FP in Java. The biggest problem with CE was just rethrowing it, and your method signature becomes a disaster. You just cannot understand how to deal with error, and so just add more thrown exceptions.
Wrong. Spring said use unchecked exceptions, C# community have looong conversation should they or not to have it, with conclusion - no. It was 2003, before FP hype.
Functional languages has alternative - Either (Scala, Haskell) / Result (Rust).
And one last but not least - checked exceptions are slow.
I said it was the biggest problem not the only reason people don't like it or the only reason languages chose to avoid it.
Spring removes the checked aspect but does that after dealing with the checked exception. Rethrowing is a feature, not a bug. It means the method explicitly declares behavior enforced by the compiler.
Nope. Checked exceptions aren't slow. Since the JVM has no knowledge of their existance they have the exact same overhead as regular exceptions.