DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workkloads.

Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • Finally, an ORM That Matches Modern Architectural Patterns!
  • The Hidden Costs of Lombok in Enterprise Java Solutions
  • Java Caching Strategies With NCache
  • Harnessing the Power of SIMD With Java Vector API

Trending

  • Understanding Java Signals
  • Microsoft Azure Synapse Analytics: Scaling Hurdles and Limitations
  • Beyond ChatGPT, AI Reasoning 2.0: Engineering AI Models With Human-Like Reasoning
  • Create Your Own AI-Powered Virtual Tutor: An Easy Tutorial
  1. DZone
  2. Coding
  3. Java
  4. Everything Bad in Java Is Good for You

Everything Bad in Java Is Good for You

Nulls and checked exceptions are often promoted as "bad things" in Java, this isn't the case. Both carry significant advantage over the alternatives.

By 
Shai Almog user avatar
Shai Almog
DZone Core CORE ·
Jun. 13, 23 · Opinion
Likes (17)
Comment
Save
Tweet
Share
9.2K Views

Join the DZone community and get the full member experience.

Join For Free

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 well-being. I love the premise of disrupting the conventional narrative and was reminded of that constantly when debating some of the more controversial features and problems in Java. It’s a feature, not a bug…

One of my favourite things about Java is its tendency to move slowly and deliberately. It doesn’t give us what we want right away. The Java team understands the requirements and looks at the other implementations, then learns from them. 

I’d say Java’s driving philosophy is that the early bird is swallowed by a snake.


Checked Exceptions

One of the most universally hated features in Java is checked exceptions. They are the only innovative feature Java introduced as far as I recall. Most of the other concepts in Java existed in other languages, checked exceptions are a brand new idea that other languages rejected. They aren’t a “fun” feature, I get why people don’t like them. But they are an amazing tool.

The biggest problem with checked exceptions is the fact that they don’t fit nicely into functional syntax. This is true for nullability as well (which I will discuss shortly). That’s a fair complaint. Functional programming support was tacked onto Java and in terms of exception handling it was poorly done. The Java compiler could have detected checked exceptions and required an error callback. This was a mistake made when these capabilities were introduced in Java 8. E.g. if these APIs were better introduced into Java we could have written code like this:

Java
 
api.call1()
    .call2(() -> codeThatThrowsACheckedException())
    .errorHandler(ex -> handleError(ex))
    .finalCall();


The compiler could force us to write the errorHandler callback if it was missing which would satisfy the spirit of the checked exceptions perfectly. This is possible because checked exceptions are a feature of the compiler, not the JVM. A compiler could detect a checked exception in the lambda and require a specially annotated exception handling callback. 

Why wasn’t something like this added? 

This is probably because of the general dislike of checked exceptions. No one attempted to come up with an alternative. No one likes them because no one likes the annoying feature that forces you to tidy up after yourself. We just want to code, checked exceptions force us to be responsible even when we just want to write a simple hello world…

This is, to a great extent, a mistake… We can declare that main throws an exception and create a simple hello world without handling checked exceptions. In large application frameworks like Spring, checked SQLException is wrapped with a RuntimeException version of the same class. You might think I’m against that but I’m not. It’s a perfect example of how we can use checked exceptions to clean up after the fact. Cleanup is performed internally by Spring, at this point the exception-handling logic is no longer crucial and can be converted to a runtime exception.

I think a lot of the hate towards the API comes from bad versions of this exception such as MalformedURLException or encoding exceptions. These exceptions are often thrown for constant input that should never fail. That’s just redundant and bad use of language capabilities. Checked exceptions should only be thrown when there’s cleanup we can do. That’s an API problem, not a problem with the language feature.

Null

Pouring hate on null has been trending for the past 15+ years. Yes, I know that quote. I think people misuse it.

Null is a fact of life today, whether you like it or not. It’s inherent in everything: databases, protocols, formats, etc. Null is a deep part of programming and will not go away in the foreseeable future.

The debate over null is pointless. The debate that matters is whether the cure is better than the disease and I’m yet unconvinced. What matters isn’t if null was a mistake, what matters is what we do now.

To be fair, this directly correlates to your love of functional programming paradigms. Null doesn’t play nicely in FP which is why it became a punching bag for the FP guys. But are we stepping back or stepping forward?

Let’s break this down into three separate debates:

  • Performance
  • Failures
  • Ease of programming

Performance

Null is fast. Superfast. Literally free. The CPU performs a null check for us and handles exceptions as interrupts. We don’t need to write code to handle null. The alternatives can be very low overhead and can sometimes translate to null for CPU performance benefits. But this is harder to tune. 

Abstractions leak and null is the way our hardware works. For most intents and purposes, it is better.

There is a caveat. We need the ability to mark some objects as non-null for better memory layout (as Valhalla plans to do). This will allow for better memory layout and can help speed up code. Notice that we can accomplish this while maintaining object semantics, a marker would be enough.

I would argue that null takes this round.

Failures

People hate NullPointerException. This baffles me. 

NullPointerException is one of the best errors to get. It’s the fail-fast principle. The error is usually simple to understand and even when it isn’t; it isn’t far off. It’s an easy bug to fix. The alternative might include initializing an empty object which we need to verify or setting a dummy object to represent null. 

Open a database that has been around long enough and search for “undefined”. I bet it has quite a few entries… That’s the problem with non-null values. You might not get a failure immediately. You will get something far worse. A stealth bug that crawls through the system and pollutes your data.

Since null is so simple and easy to detect there’s a vast number of tools that can deal with it both in runtime and during development. When people mention getting a null pointer exception in production I usually ask: what would have been the alternative?

If you could have initialized the value to begin with then why didn’t you do it? 

Java has the final keyword, you can use that to keep non-null stateful values. Mutable values are the main reason for uninitialized or null values. It’s very possible that a non-null language wouldn’t fail. But would its result be worse? 

In my experience, corrupt data in storage is far worse. The problem is insidious and hides under the surface. There’s no clue as to the origin of the problem and we need to set “traps” to track it down. Give me a fail-fast any day.

In my opinion, null has this one hands down…

Ease of Programming

An important point to understand is that null is a requirement of modern computing. Our entire ecosystem is built on top of null. Languages like Kotlin demonstrate this perfectly, they have null and non-null objects.

This means we have duplication. Every concept related to objects is expressed twice, and we need to maintain semantics between null and non-null. This raises the complexity bar for developers new to such languages and makes for some odd syntax.

This in itself would be fine if the complexity paid off. Unfortunately, such features only resolve the most trivial non-issue cases of null. The complex objects aren’t supported since they contain null retrieved from external sources. We’re increasing language complexity for limited benefit.

Boilerplate

This used to be a bigger issue, but looking at a typical Java file vs. TypeScript or JavaScript, the difference isn’t as big. Still, people nitpick. A smart engineer I know online called the use of semicolons in languages: "Laziness". 

I don’t get that. I love the semicolon requirement and am always baffled by people who have a problem with that. As an author it lets me format my code while ignoring line length. I can line break wherever I want, the semicolon is the part that matters. If anything, I would have loved to cancel the ability to write conditional statements without the curly braces e.g.:

Java
 
if(..) x();
else y();


That’s terrible. I block these in my style requirements; they are a recipe for disaster with an unclear beginning or end. 

Java forces organization, this is a remarkable thing. Classes must be in a specific file and packages map to directories. This might not matter when your project is tiny, but as you handle a huge code base, this becomes a godsend. You would instantly know where to look for clues. That is a powerful tool. Yet, it leads to some verbosity and some deep directory structures. But Java was designed by people who build 1M LoC projects, it scales nicely thanks to the boilerplate. We can’t say the same for some other languages.

Moving Fast

Many things aren’t great in Java, especially when building more adventurous startup projects. That’s why I’m so excited about Manifold. I think it’s a way to patch Java with all the “cool stuff” we want while keeping the performance, compatibility and stability we love.

This can let the community move forward faster and experiment, while Java as a platform can take the slow and steady route. 

Final Word

Conventional wisdom is problematic. Especially when it is so one-sided and presents a single-dimension argument in which a particular language feature is inferior. There are tradeoffs to be made and my bias probably shines through my words. 

However, the cookie-cutter counterpoints don’t cut it. The facts don’t present a clear picture to their benefit. There’s always a tradeoff and Java has walked a unique tightrope. Even a slight move in the wrong direction can produce a fast tumbling-down effect. Yet it maintains its traction despite the efforts of multiple different groups to display it as old-fashioned. This led to a ridiculous perception among developers of Python and JavaScript as “newer” languages.

I think the solution for that is two-fold. We need to educate about the benefits of Java's approach to these solutions. We also need solutions like Manifold to explore potential directions freely. Without the encumberment of the JCP. A working proof of concept will make integrating new ideas into Java much easier.

Java (programming language) Boilerplate code Functional reactive programming Java Decompiler Java performance

Published at DZone with permission of Shai Almog, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • Finally, an ORM That Matches Modern Architectural Patterns!
  • The Hidden Costs of Lombok in Enterprise Java Solutions
  • Java Caching Strategies With NCache
  • Harnessing the Power of SIMD With Java Vector API

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!