DEV Community

Cover image for Java Isn't Verbose – You Are
Adam - The Developer
Adam - The Developer

Posted on

Java Isn't Verbose – You Are

Every week, some developer crawls out of their legacy codebase to announce that Java is "too verbose." They'll tweet about it. They'll mention it in code reviews. They'll use it as an excuse for why their 200-line method exists.

But here's the thing: when I actually look at their code, it's not Java's fault. It's theirs.


The Real Problem? Your Fingers

Let me show you what I mean.
Here's the kind of code people write and then blame the language for:

User user = new User();
user.setName("Alice");
user.setAge(30);
user.setEmail("alice@example.com");
user.setActive(true);

repo.save(user)
Enter fullscreen mode Exit fullscreen mode

Six lines to create one object.
Now here's the same thing written by someone who's actually read a release note since 2015:

var user = new User("Alice", 30, "alice@example.com", true);
repo.save(user)
Enter fullscreen mode Exit fullscreen mode

Or hell, if you're not storing the reference:

repo.save(new User("Alice", 30, "alice@example.com", true));
Enter fullscreen mode Exit fullscreen mode

One line. Done. You wrote six lines to accomplish what one could do – and you have the audacity to call the language verbose?

You Are The Boilerplate

Let's talk about the real sources of "verbosity" in Java codebases. Spoiler: it's you.

Still Writing Getters and Setters in 2025

If you're manually typing out getX() and setX() methods in 2025, you're not a professional developer — you're a
human code generator. Records have been around since Java 14. They're stable. They're production-ready. Use them:

public record User(String name, int age, String email, boolean active) {}
Enter fullscreen mode Exit fullscreen mode

That's it. That's the entire class. You get constructors, getters, equals(), hashCode(), and toString() for free. But sure,
keep writing 50 lines of boilerplate and complaining about "verbosity."

Avoid var Because "It's confusing"

No mate, it's not confusing, but you know what is? this:

Map<String, List<ComplexBusinessObject>> groupedData = complexService.getGroupedData();
Enter fullscreen mode Exit fullscreen mode

vs this:

var groupedData = complexService.getGroupedData();
Enter fullscreen mode Exit fullscreen mode

This type is right there in the method name. If your method names are so bad that removing the explicit type declaration makes code "unreadable", your problem isn't var – it's your inability to write clear code.

Pattern Overload for Trivial Logic

We've all seen it: the UserFactory, the UserBuilder, the UserManager, the UserService, the UserHelper, the UserUtil.
5 classes to accomplish what should be a constructor and 2 methods.

Design patterns exist to solve actual problems, not to make your codebase look "enterprise". If you're creating a factory pattern to instantiate a POJO with 3 fields, you're not writing good Java – you're writing a resume.

Ignoring Modern Language Features

Java has evolved. Significantly. But some developers are still writing like it's 2005:

  • Switch expressions (Java 14): Clean pattern matching instead of verbose if-else chains
  • Text blocks (Java 15): Multiline strings without the nightmare of concatenation
  • Pattern matching for instanceof (Java 16): No more casting after type checks
  • Sealed classes (Java 17): Controlled inheritance hierarchies
  • Record patterns (Java 21): Destructuring records directly in pattern matches

These aren't experimental features. They're here. They work. And they make Java significantly less verbose. But you'd know that if you'd read a changelog in the last 5 years.

Modern Java Actually Slaps

Let's be real: Java from version 14 onwards is a completely different beast than Java 8. Hell, it's a different species than Java 5.

Here's what you can do now:

// Records for data classes
public record Product(String name, double price) {}

// Pattern matching with switch expressions
String describe(Object obj) {
    return switch (obj) {
        case Integer i -> "Number: " + i;
        case String s -> "String: " + s;
        case Product(var name, var price) -> "Product: " + name + " with price $" + price;
        case null, default -> "Unknown";
    }
}

// Text blocks for readable multiline strings
var json = """
        {
            "name": "John",
            "age": 30,
            "city": "New York"
        }
        """;

// Streams and lambdas for functional-style operations
var adults = users.stream()
.filter(u -> u.age() >= 18)
.toList();
Enter fullscreen mode Exit fullscreen mode

Is this verbose? No. Is it clear? Yes. Is it modern? Yes.
The language has grown up. Have you?

The Verbosity is Coming From Inside the House

Here's the uncomfortable truth: most "verbose Java" isn't a language problem — it's a skill problem.

You're verbose because:

  • You learned Java once in 2010 and never updated your knowledge
  • You cargo-cult patterns you saw in ancient Spring codebases
  • You think "enterprise" means "uses as many classes as possible"
  • You're genuinely unaware that Java has had major improvements in the last decade
  • You're too lazy to refactor but not too lazy to complain

The language gave you tools. You just refuse to use them.

" But Python/JavaScript/Go Is Less Verbose! "

Oh, here we go again, the grass-is-greener crowd.

Yes, Python lets you write user = User("Alice", 30) without type annotations. Congratulations, you've discovered dynamic typing. Now wait until runtime to find out you passed a string where an integer should be, and enjoy debugging production.

JavaScript? Sure, it's concise:

const user = { name: "Alice", age: 30 }
Enter fullscreen mode Exit fullscreen mode

It's also a chaotic hellscape where typeof null === "object" and everything is secretly a string. Hope you enjoy "2" + 2 === "22" and 3 different ways to declare variables depending on which year you learned the language.

Go is intentionally minimal, which is great until you need generics (oh wait, they just added those in 2022 after a decade of people copy-pasting code), or want a robust standard library for anything beyond network servers.

Every language makes tradeoffs. Java chose type safety, backwards compatibility, and explicit contracts. If that's "verbose" to you, you're really saying "I don't want to think about types" – which is fine! Just don't pretend it's a language flaw when it's design philosophy you disagree with.

The AbstractSingletonProxyFactoryBean Strawman

Let's address the elephant in the room: Spring Framework's infamous AbstractSingletonProxyFactoryBean.

Yes, it exists. Yes, it's ridiculous. No, you don't write that – Spring does. And you probably never interact with it directly.

Blaming Java for Spring's internal implementation details is like blaming English because legal documents are hard to read. The language isn't the problem; it's what certain frameworks built with it.

In fact, Modern Spring Boot applications are concise and straightforward to understand as hell, like, what average developer who's worked with REST frameworks wouldn't understand this???

    @RestController
    @RequestMapping("/users")
    public class ApiController {
        private final UserRepository userRepository;

        public UserController(UserRepository userRepository) {
            this.userRepository = userRepository;
        }

        @GetMapping("/{id}")
        public User getUser(@PathVariable Long id) {
            Optional<User> user = userRepository.findById(id);
            return userRepository.findById(id)
                    .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "User not found"));
        }
    }
Enter fullscreen mode Exit fullscreen mode

Where's the verbosity?? Where are the XML files?? Where's the abstract factory singleton proxy whatever the hell that is?

Oh right, it's 2025, not 2008.

Your Null Checks Are Embarrassing And Shameful

Speaking of outdated practices, let's talk about this crime against humanity:

 public String getUserEmail(User user) {
    if (user != null) {
        if (user.getEmail() != null) {
            return user.getEmail();
        }
    }

    return null;
 }
Enter fullscreen mode Exit fullscreen mode

Four lines, two null checks, one return value. This is what happens when developers learned defensive programming in 2002 and stopped learning.

Here's the modern approach:

    public Optional<String> getUserEmail(User user) {
        return Optional.ofNullable(user).map(User::getEmail);
    }
Enter fullscreen mode Exit fullscreen mode

Or if you're using records and keeping things null-safe by design, you don't even need Optional:

    public String getUserEmail(User user) {
        return user.email(); // Records can't be null by design
    }
Enter fullscreen mode Exit fullscreen mode

But sure, keep writing defensive null checks everywhere and then complaining that Java is "too safe" and therefore "verbose."

The Real Verbosity is Your Code Reviews

You wanna know what's truly verbose? That's right, your code reviews.

"This method is too long" – it's 8 lines, Karen.

"We should extract this into a separate class" – it's a single calculation used once.

"Let's make this more enterprise" – this is a startup, not Boeing.

The obsession with over-abstraction, over-engineering, and "clean code" taken to religious extremes has caused more verbosity than the Java language ever did.

Uncle Bob didn't say every method should be 3 lines, did he? He said code should be readable. Sometimes that's 3 lines. Sometimes that's 20. Learn the difference.

Conclusion: Learn or Leave

Java isn't verbose. Your mindset is.
How about doing us a favor by leaving to save us some time from re-reading your code if you refuse to grow your 2005 mindset.

If you're still writing Java like it's the era of XML config files and AbstractSingletonProxyFactoryBeans, that's on you. Modern Java is expressive, concise, and powerful – but only if you actually bother to write modern Java.

So either update your skills, or go back to your build.xml file where you belong. Some of us have one-liners to write.


Now if you'll excuse me, I have a 47-line "service layer" to refactor into 3 lines of actual logic

Top comments (13)

Collapse
 
medikoo profile image
Mariusz Nowak

new User("Alice", 30, "alice@example.com", true);

With this notation you significantly loose on code readability. What true actually does here?

In other language it's solved with named params or object literals passed as params. Not sure what are the options for Java

Collapse
 
adamthedeveloper profile image
Adam - The Developer

I agree. but there are idiomatic ways to make this cleaner.
You can use the builder pattern:

User user = User.builder()
    .name("Alice")
    .age(30)
    .email("alice@example.com")
    .isActive(true)
    .build();
Enter fullscreen mode Exit fullscreen mode

it’s a bit verbose, but that’s the tradeoff: explicit over magical.
Every language picks a poison — Java just happens to prefer readability over shorthand.

Collapse
 
medikoo profile image
Mariusz Nowak

That looks as best approach to me. Thanks for pointing this out

Collapse
 
zir_teq_9fb58989d02780bdc profile image
Zir Teq

I mostly agree with the article, but still..

I think I found the verbosity in the example. It's in the u -> u of the lambda.
In Kotlin or Scala we have defaults for that and it's six to three times shorter (just _ or it).

In Java we can optimize it to .filter(User::isAdult), but then we get to the actual verbosity problem. Four new lines, including the one conventional empty line (thankfully not two as in Python) before or after the declaration:
public boolean isAdult() {
return age >= 18;
}

In C# that's a one-liner and you don't even need an empty line one before or after, without breaking any styling conventions:
public bool isAdult => this.Age > 18;

As for Uncle Bob; he now found Clojure, which if well written, isn't even fair to compare in terms of verbosity.
(->> users (filter #(>= (:age %) 18)))

Collapse
 
aviad_cohen_f9fe84a29ac88 profile image
Aviad Cohen • Edited

Regarding your C# preferred-one-liner, how about that:
Predicate<User> isAdult = user -> user.getAge() > 18
One-liner (for those who appreciate it, as if it's the holy grail of coding), using common-known terminology (Predicate), I would also claim it's more concise: Who's this? Better be expressive: it's a 'user'.
(and yes, you can also spare the getAge getter, which no one actually writes (or has to read) nowadays: Predicate<User> isAdult = user -> user.age > 18 ; I personally prefer the getter.)

Not sure who sees this line (->> users (filter #(>= (:age %) 18))) and feels a lack of verbosity is something to appreciate... :)

Collapse
 
schmoris profile image
Boris

"Let's make this more enterprise" – this is a startup, not Boeing.

Literally lol'd!

Collapse
 
morganllewellynjones profile image
morganllewellynjones

These are good points, but a language is not just a language, it is an ecosystem.

I don't think most Java developers are start-up developers attempting to emulate enterprise. A lot of them are enterprise developers. You read a lot more Java than you write in these kinds of situations.

There's also a lot of conventions that can be dropped at this point, but have not been. Pick up any book on Java written in the last few years and it will still recommend writing getters and setters for all of your properties (they will mention records, but most of their examples will not use records). Many modern Java examples will also continue to avoid var. When senior engineers who guide new engineers learning into the ecosystem and existing codebases continue to push for the use of old practices, it matters less that it is technically possible to write code in a cleaner and more modern way. Conventions take a long time to shake.

Collapse
 
vulinh64 profile image
Linh Nguyen

Some additional information I need to add (so that I created an account):

  1. That number 47 sounds like this is a post written, or at least assisted by A.I.

  2. Records can be null

  3. Using Optional everywhere is an invitation to performance hit, may not be noticeable, but it might add up. I'd suggest using Optional only for nested null check, and wait until JEP 401 is finalized

  4. case null, default -> "Unknown" can be just default -> "Unknown", default can handle null case.

Other than that, this post is really, really cathartic. To the hell with the old bloated development mentality.

Collapse
 
adamthedeveloper profile image
Adam - The Developer

haha i really need to publicly address the " Records cannot be null " part because alot of people are confused by it. what i was trying to say is, if you design your data to be non-null by design ( validation, etc. ), then you don't even need the Optional.

and thanks for pointing out 3 & 4.

and no AI there, still human here. but ill take that as a compliment lol.

Collapse
 
aviad_cohen_f9fe84a29ac88 profile image
Aviad Cohen

Great article!
Almost as if someone got into my head and spelled everything lies there out, to the extreme.. :)

I would claim an even more extreme statement: verbosity isn't always a bad thing, or something to avoid.
Sure, you can exaggerate with it and be too verbose, and that might harm your code, but being less and less verbose is also not a good place to be.

Eventually, most of the time, most of us usually read code (and not write it). Verbosity, to a certain extent, serves this purpose.
No one wants to read cryptic lines of code that only one person at the office might understand (regex people - I am talking to you!).

I teach students Java technology and ecosystem.
As an example, I show them this line:
class Student extends Person implements Comparable<Student>, HasName
It's (almost) a valid English sentence, expressing the intent and meaning behind every word (Students extend a person; it aims to implement something called Comparable, etc.)

IMO, the alternative using common developer-characters (:) instead of pure, simple, meaningful English words (extends, implements) is not something to be proud of, or push for.

We gain nothing from this sentence Student : Person, IComparable, IHasName, but even my mother can read Student extends Person implements Comparable<Student>, HasName
:)

Collapse
 
chris_zhao_b04274a83d2b09 profile image
Chris Zhao

Just use Kotlin. You still get the full benefits of the Java ecosystem and the JVM, but with a much better developer experience 🤷

Collapse
 
kekwlekw profile image
FkUnder Collins

Java is verbose compared to other languages (other than C & C++ at least)

Collapse
 
sadiul_hakim profile image
Sadiul Hakim

Java is a high-level language.