← Back to context

Comment by marginalia_nu

3 days ago

I think the author is sleeping on Java assertions.

I really like the feature, and it's really one of the features I feel Java got right.

The syntax is very expressive, and they can easily be made to generate meaningful exceptions when they fail.

It's also neat that it gives the language a canonical way of adding invariant checks that can be removed in production but run in tests or during testing or debugging (with -da vs -ea).

You could achieve similar things with if statements, and likely get similar performance characteristics eventually out of C2, but this way it would be harder to distinguish business logic from invariant checking. You'd also likely end up with different authors implementing their own toggles for these pseudo-assertions.

I'm quite surprised that he said asserts are not found in production code. Is that really so? I rarely write Java, but in C code we use asserts (in production code) all the time. It's not uncommon for functions to contain 2 or 3 asserts.

  • I very rarely see assertions in "real" Java code; I think the author is right - in fact the place I see them the most often is in unit tests where they've been used by mistake in place of an assertion library's methods!

    I don't know why they're not more popular.

  • I think they're used less because the main reason for using them in C isn't a consideration in Java, and also they don't accomplish the same thing.

    In C, asserts are used as sanity checks, and when one is violated, there's often reasonable suspicion that that memory corruption has occurred, or that memory corruption will occur if the code proceeds in the current state. Aborting the process, leaving a core dump for analysis, and starting fresh is often the safest thing to do to avoid the unpredictable results of corrupted state, which can be insidiously subtle or insanely dramatic. In my experience writing server-side C++, we always ran it in production with asserts enabled, because code that continued to run after memory was corrupted led to the the bugs that were the most destructive and the most mysterious.

    Memory corruption is rare enough in Java that 99.9% of code completely ignores the possibility. Also, if you did suspect memory corruption in a Java program, an assert wouldn't help, because it would only throw a runtime exception that would probably get caught and logged somewhere, and the process would continue serving requests or whatever else it was doing.

  • If you're only doing like CRUD endpoints, they may be less useful, but that's hardly the extent of Java production code. I certainly use asserts in production code quite a lot in Java, though the use biases toward more low level functions, rarely in high level application logic.

What are the pros of making this a keyword vs just a standard function?

  • At the time the feature was added, there was no way to make a parameter to a function be lazily evaluated. Something like `assert(condition, "error: " + stuff)` would eagerly concatenate the string even when the condition is always true (which it should be). Nowadays, the error parameter can be specified as a lambda, which can potentially be optimized to be just as cheap as the existing assert feature.

  • You can disable them at runtime (e.g. in prod) to avoid the performance overhead once you're satisfied the codebase is thoroughly enough tested.

    For some things like requiring arguments to be non-null static checks with annotations have superseded them (in a confusing way inevitably - I think there are three different common non-nullness annotations).

  • The pros are that it can generate better error messages, without putting that responsibility on the programmer. Something that would otherwise require a preprocessor or some form of metaprogramming.