37

I've seen this question but have a few more questions about the usage of the assert keyword. I was debating with a few other coders about using assert. For this use case, there was a method that can return null if certain prerequisites are met. The code I wrote calls the method, then asserts it doesn't return null, and continues to use the returned object.

Example:

class CustomObject {
    private Object object;

    @Nullable
    public Object getObject() {
        return (object == null) ? generateObject() : object;
    }
}

Now imagine I use it like this:

public void useObject(CustomObject customObject) {
    object = customObject.getObject();
    assert object != null;
    // Do stuff using object, which would throw a NPE if object is null.
}

I was told I should remove the assert, that they should never be used in production code, only be used in testing. Is that true?

akuzminykh
  • 4,522
  • 4
  • 15
  • 36
Big_Bad_E
  • 947
  • 1
  • 12
  • 23
  • 20
    By default, assertions are disabled. You have to explicitly enable them at runtime via `-ea` or equivalent. – jarmod Mar 16 '20 at 20:13
  • Related question on Software Engineering: https://softwareengineering.stackexchange.com/q/137158/187318 (although it is quite old, so the accepted answer there still recommends an external library, which is no longer necessary since the introduction of `Objects.requireNonNull` in Java 8). – Hulk Mar 17 '20 at 08:19
  • This question title is way overly broad, but the question stated in the body is much more narrow, and per the answers is fully handled by helper-functions. – smci Mar 17 '20 at 12:35
  • To be clear, you're asking whether ***client*** code should implement non-null checks/asserts on objects. (That's different from asking whether the ***library*** code itself should guarantee objects cannot be null, or assert that there). – smci Mar 17 '20 at 12:38
  • *"I was told assert should NEVER be used in production code, only in testing."* Depends on what the expectation of test coverage and correctness is, and very domain-specific (just e-commerce? airplane control?) Is this your library and are you happy with its level of test coverage? – smci Mar 17 '20 at 12:40
  • I believe you are asking about _whether_ assertions should be used in production code, not _how_. Look for articles on the software architecture strategy "fail fast" (aka "fail early"). IMO it has a place in many - but not all! - production environments, and may help you figure out whether assertions are a good idea or not in your particular use cases. – davidbak Mar 17 '20 at 14:31
  • Also please note that this can be language and community-specific: people use asserts in production code all the time in other languages, especially dynamic languages. – Jared Smith Mar 17 '20 at 19:37
  • 1
    Possible duplicate: *[When should assertions stay in production code?](https://stackoverflow.com/questions/17732/when-should-assertions-stay-in-production-code)* – Peter Mortensen Mar 17 '20 at 20:53
  • After more than 11 years and [19,101,075 questions](https://stackoverflow.com/questions?tab=Newest), what is the chance that such a question has not been asked before? – Peter Mortensen Mar 17 '20 at 20:59
  • Almost exact duplicate (as C# is an improved version of Java and/or a case of plagiarism): *['Assert' statements in .NET production code](https://stackoverflow.com/questions/1818999)* – Peter Mortensen Mar 17 '20 at 21:01
  • Tagged with Java (tough more ***can*** than ***should*** / ***should not***): *[Can assertions be used in production or only while developing?](https://stackoverflow.com/questions/14985328)* – Peter Mortensen Mar 17 '20 at 21:08
  • Stack Overflow question *[Java assertions underused](https://stackoverflow.com/questions/298909)* has [an answer](https://stackoverflow.com/questions/298909/java-assertions-underused/54778330#54778330) containing *"This maxim of "**Assertions** aren't generally used in **production code**”, though common, is **incorrect**."* (my emphasis) – Peter Mortensen Mar 17 '20 at 21:17

6 Answers6

28

Use Objects.requireNonNull(Object) for that.

Checks that the specified object reference is not null. This method is designed primarily for doing parameter validation in methods and constructors, [...]

In your case that would be:

public void useObject(CustomObject customObject) {
    object = customObject.getObject();
    Objects.requireNonNull(object); // throws NPE if object is null
    // do stuff with object
}

This function is made for what you want to do: explicitly mark what is not to be null. The benefit is that you find null-values right where they should not occur. You will have less troubles debugging problems caused by nulls that are passed somewhere where they shouldn't be.

Another benefit is the flexibility when using this function in contrast to assert. While assert is a keyword for checking a boolean value, Objects.requireNonNull(Object) is a function and can be embedded in code much easier.

Foo foo = Objects.requireNonNull(service.fetchFoo());

// you cannot write it in one line.
Bar bar = service.fetchBar();
assert bar != null;
service.foo(Objects.requireNonNull(service.getBar()));

// you cannot write it in one line.
Bar bar = service.getBar();
assert bar != null;
service.foo(bar);

Keep in mind that Objects.requireNonNull(Object) is only for null-checking while assert is for general assertions. So assert has different purposes: primarily testing. It has to be enabled, so you can enable it for testing and disable it in production. Use it to seperate testing-only-tests from tests, or rather checks, that are meant for production-code too.

akuzminykh
  • 4,522
  • 4
  • 15
  • 36
  • Given that you can decide at runtime whether assert operates, but you cannot determine at runtime whether the NPE is thrown by requireNonNull, what do you mean by 'you have more control with it than with assert'? – Pete Kirkham Mar 17 '20 at 07:21
  • 2
    @PeteKirkham You are right, *control* was the wrong word for that. I was more talking about the flexibility in syntax. In addition: [here](https://stackoverflow.com/questions/45632920/why-should-one-use-objects-requirenonnull?noredirect=1&lq=1) can be reasons found why you would want code to throw NPEs. – akuzminykh Mar 17 '20 at 08:36
  • 1
    "don't use it for production code. It could slow down the application" It could, but it could be worth it. Java has built-in runtime checks for ensuring you never overrun an array. These are enabled even on production deployments, despite the possible performance penalty. We aren't even given a choice about it. It's not always wrong to have runtime checks in production code. – Max Barraclough Mar 17 '20 at 14:12
25

The most important thing to remember about assertions is that they can be disabled, so never assume they'll be executed.

For backward compatibility, the JVM disables assertion validation by default. They must be explicitly enabled using either the -enableassertions command line argument, or its shorthand -ea:

java -ea com.whatever.assertion.Assertion

So, it's not a good practice to rely on them.

As assertions aren't enabled by default you can never assume they will be executed when used in the code. So you should always check for null values and empty Optionals, avoid using assertions to check inputs into a public method and instead use an unchecked exception... In general do all the checks as if the assertion wasn't there.

Marcin Orlowski
  • 72,056
  • 11
  • 123
  • 141
jeprubio
  • 17,312
  • 5
  • 45
  • 56
  • Obviously bad practice to rely on them, but is it bad practice to use it in general? – Big_Bad_E Mar 16 '20 at 20:23
  • 4
    @Big_Bad_E Assertions are a debug tool that exists to make a program fail as early and as noisily as possible. It is then the job of the test suite to ensure that there is no way a program can fail *despite the assertions*. The idea is, once the test suite (it's got 100% coverage, doesn't is?!?) executes without a failure, it's save to remove the assertions as they cannot trigger anyways. Sure there's a bit of idealism in this reasoning, but that's the idea. – cmaster - reinstate monica Mar 17 '20 at 11:34
  • This is so critical to know they are disabled by default and not intended for production. We had some of them in production to prevent a critical issue that should never happen. It went through multiple "fixes" that had an impact during development, but not so in production. At some point we realized they are disabled by default and replaced them with normal exceptions. I wonder how many production systems have them in place for the wrong reason, just because of lack of knowledge :) imo it would be better if they wouldn't exist at all or if the IDE would give hints at least – Marian Klühspies Sep 22 '22 at 09:01
13

Surely what you are told is a blatant lie. Here's why.

Assertions are disabled by default if you just execute standalone jvm. When they are disabled, they have zero footprint, hence they will not affect your production application. However, they are probably your best friends when developing and testing your code, and most of testing framework runners enable assertions (JUnit does), so your assertion code is executed when you run your unit tests, helping you detect any potential bugs earlier (e.g. you can add asserts for some business logic boundary checks, and that will help detect some code which uses inappropriate values).

That said, as the other answer suggests, for exactly that reason (they are not always enabled) you cannot rely on assertions to do some vital checks, or (especially!) maintain any state.

For an interesting example of how you could use asserts, have a look here - at the end of the file there's a method singleThreadedAccess() which is called from the assert statement on line 201 and is there to catch any potential multithreaded access in tests.

Dmitry Pisklov
  • 1,196
  • 1
  • 6
  • 16
5

The other answers already cover this well enough, but there are other options.

For example, Spring has a static method:

org.springframework.util.Assert.notNull(obj)

There are other libraries with their own Assert.something() methods as well. It's also pretty simple to write your own.

However, keep in mind what exceptions you throw if this is a web service. The previous method mentioned, for example, throws an IllegalArgumentException which by default in Spring returns a 500.

In the case of a web service, this is often not an internal server error, and should not be a 500, but rather a 400, which is a bad request.

Christopher Schneider
  • 3,745
  • 2
  • 24
  • 38
  • 1
    If the "Assert" method does not immediately crash the process, throwing some kind of exception instead, then it's not an assert. The whole point of `assert` is, to get an immediate crash with a core file produced so that you can do a post-mortem with your favorite debugger right at the spot where the condition was violated. – cmaster - reinstate monica Mar 17 '20 at 11:42
  • Asserts don't immediately crash a process. They throw an Error, which may be caught. Unhandled exceptions will crash your tests just as well as errors. You can argue semantics if you want. If you so desire, write a custom assert that throws an Error instead of exception. The fact is, in the overwhelming majority of cases, the appropriate action is to throw an exception rather than an error. – Christopher Schneider Mar 17 '20 at 14:14
  • Ah, Java does indeed define `assert` to throw. Interesting. The C/C++ version doesn't do such a thing. It immediately raises a signal that a) kills the process, and b) creates a core dump. It does this for a reason: It's dead simple to debug such an assertion failure because you still have the entire information about the call stack available. Defining `assert` to throw an exception instead, which may then be caught (un)intentionally programmatically, defeats the purpose, imho. – cmaster - reinstate monica Mar 17 '20 at 14:24
  • Just as there are some (or many) weird things in C and C++, there are some in Java. One of them is `Throwable`. They can always be caught. Whether or not that's a good thing or not, I don't know. I guess it depends. Many Java programs are web services, and it would be undesirable for it to crash for almost any reason, so just about everything is caught and logged. One of the great things is the stack trace, and that's usually enough to diagnose the reason for an exception or error by itself. – Christopher Schneider Mar 17 '20 at 14:53
4

Use asserts liberally whenever doing so helps catching programming mistakes i.e. bugs.

Do not use assert to catch something that might logically happen i.e. badly formatted input. Use assert only when the error is unrecoverable.

Do not put any production logic in the code that runs when the assertion is checked. If your software is well written this is trivially true but if it's not then you might have subtle side effects and different overall behavior with assertions enabled and disabled.

If your company has "testing code" and "production code" doing the same thing but as different code bases (or different stages of editing), get out of there and never come back. Trying to fix that level of incompetence is probably a waste of your time. If your company doesn't put any assert statement outside of the code of the tests, kindly tell them that asserts are disabled in the production build and that if they aren't, fixing that mistake is now your first priority.

The value of asserts is precisely to be used inside the business logic and not only the test suite. This makes it easy to churn out many high level tests that don't have to explicitly test many things to go through big chunks of your code and trigger all these assertions. In a few of my projects typical tests didn't even really assert anything, they just ordered a calculation to happen based on specific input and this caused hundreds of assertions to be checked and problems to be found even in tiny pieces of logic deep down.

Kafein
  • 357
  • 1
  • 7
3

You can use assert any time. The debate come is when to use. For example in the guide :

  • Do not use assertions for argument checking in public methods.
  • Do not use assertions to do any work that your application requires for correct operation.
Gatusko
  • 2,503
  • 1
  • 17
  • 25