Why should assertions not be used for argument checking in public methods?
Asked Answered
D

8

12

Well, I have never really worked with assertions in my limited experience with Java and was wondering why I have read on a lot of sites and a lot of books that deal with assertions, the same warning that assert statements should not be used for argument checking in public methods?

I was wondering whether this had something to do with the order of execution of the assert statement relative to the other statements in Java.

Dilative answered 12/12, 2012 at 3:53 Comment(1)
Note: This issue has nothing to do with the public keyword in Java. It has to do with "public interfaces" or APIs that will be made available for use for others outside of your programming team.Enchorial
E
6

The intent of assertions is to check your program logic -- an assertion failure is a "Stop everything -- there's a bug!" indication. In particular, an assertion failure indicates "there's a bug here", but "here" is somewhere internal to your code, and the cause of the failure can only really be determined by examining your code (which the user of your API cannot and should not be expected to do).

When you get bad data across an API, you want to indicate "Hey! You gave me bad data!" IllegalArgumentException and its kin are the way to indicate that.

(But note that there's nothing wrong with using assertion checks on parameters within your code -- where you're not supporting a truly "public" API that will be used by people outside your team.)

But this does bring up another point: To the extent reasonable/possible, you should "catch" internal exceptions of the IllegalArgumentException ilk that may occur due to your own bugs and convert them into FatalError exceptions or some such, so the user of your API isn't led to go looking for a bad parameter on his part when there's a bug in your code.

(Also note the distinction here between public -- the Java keyword -- and "public interface" -- meaning some interface that is made available as a "formal" API to be used by individuals outside your programming team. It's the latter case we're worried about here.)

Enchorial answered 12/12, 2012 at 4:11 Comment(1)
could you please help me in this related question - #56301162 ? thanks.Miserly
C
5

Informally, argument checking and assertions serve different purposes:

  • Argument checking is to detect situations when the person calling your method does something incorrectly, while
  • Assertions are used to detect situations when you do something incorrectly.

Essentially, when you assert a condition

assert val < total;

the check conveys the following plain-English thought to the readers of your code: "I checked my code, and according to my reasoning, I am certain that val will always be less than total".

When you check an argument val, on the other hand,

if (val >= total) throw new InvalidArgumentException("val");

your code says that "the caller has forgotten to ensure that the val is less than total".

These are two different thoughts, so it is natural to employ two different ways to convey them in your code.

Coburn answered 12/12, 2012 at 4:3 Comment(1)
could you please help me in this related question - #56301162 ? thanks.Miserly
G
4

As per programming with Assertions

Argument checking is typically part of the published specifications (or contract) of a method, and these specifications must be obeyed whether assertions are enabled or disabled. Another problem with using assertions for argument checking is that erroneous arguments should result in an appropriate runtime exception (such as IllegalArgumentException, IndexOutOfBoundsException, or NullPointerException). An assertion failure will not throw an appropriate exception.

Graziano answered 12/12, 2012 at 3:56 Comment(2)
The link is broken ("Page not found").Elan
@PeterMortensen Updated. Thanks!Graziano
S
4

Mike gave an excellent answer that, unfortunately, is seldom defended in the Java literature. Some extra support to Mike's views:

The vast majority of the Java literature propagates the dogma that you should not use assert to check public method arguments. In other words, they are saying that assert should not be used to check preconditions on public methods, and that you should instead use explicit if (!precond) throw SomeException(); instructions. The standard Java library is full of examples of this policy.

The arguments to support this seem to be:

  • Responsibility for satisfying the preconditions is with the function caller (the client's code) and so not "your" code (the supplier's code).
  • Assertion checking is optional, so you'd better force the checking.

Well, this seems a very patronizing attitude to me. The clients of your code are programmers, just like you. Satisfying the preconditions is the clients' responsibility, of course, and if the clients don't do it, it's a bug; the clients' bug, not yours. Certainly, you expect your clients to check their programs with enabled assertions, don't you?

But once they are convinced of their program's correctness, why should you still force your useless exceptions on them?

Now, look at it from the client's perspective. You're using String.charAt. The documentation for this method tells you that

public char charAt​(int index)

Returns the char value at the specified index. An index ranges from 0 to length() - 1. [...]

The precondition is clearly stated.

But later it adds

Throws: IndexOutOfBoundsException - if the index argument is negative or not less than the length of this string.

So, if you know for sure that your index is within bounds, will you still put your call inside a try ... catch? Notice that the documentation does not really tell you that the exception won't be thrown if you respect the precondition, but of course that's what you expect, isn't it?

So, you're sure that the index is within bounds, you may have even asserted that previously, and you won't waste time and space with a useless try that will never catch anything, but the String will still be wasting time checking that you were behaving well.

The way I see it,

  • Assertions should be used to check conditions that are entirely under the control of a programmer, be it the client or the supplier.
  • Explicit conditional instructions with exceptions (or another error signalling device like an error return code) should be used to check conditions that depend on uncontrollable external factors, like user input, unpredictable operating system restrictions, etc.

This is what I teach my students. I don't tell them. It's not a dogma. I let them know this is opposite to what they'll read in most books, but I urge them to decide for themselves.

This question is not specific to Java; it is a question on programming methodology.

The approach I follow is akin to design by contract. Some languages have special syntax for supporting this style, such as the possibility of explicitly declaring preconditions, postconditions and object invariants, and these are clearly included as part of the public specification (and documentation) of the code.

Sororate answered 6/3, 2018 at 13:9 Comment(1)
You may have some interest in exploring the Computer Science Educators community as well. It's a small community, and I think you'll find that we're a welcoming bunch. Perhaps you have something to contribute there :)Kedron
M
3

For one, Java assertions are removed at runtime unless they are explicitly enabled when compiled.

Exceptions are more appropriate for parameter validation, because you are expecting to handle them, whereas assertion has the semantic meaning "this must be true at this point in the code, or else I have no idea how to handle this".

Miso answered 12/12, 2012 at 3:57 Comment(2)
Not when compiled, they are enabled/disabled during executionSteffi
I have a class where I want to check multiple inputs and then inform the user which all inputs were correct. I could do this with TestNg SoftAssert. Does Java have any in built assertion features like soft assert which will always be executed ?Miserly
M
2

The notion that assertions should not be used for argument checking in public methods is just plain wrong. Just because you read something in a book does not mean that it is correct.

Argument checking in public methods falls squarely within the general category of checking against bugs, therefore it should be treated as such, and the mechanism that we already have for catching bugs is assertions.

If the public interface of a method says "the 'index' argument to this method should never be negative" then invoking it with a negative index is a bug, and the following things hold true:

  1. It should never happen in a production environment. (Testing should guarantee this.)

  2. Even if it was to happen in a production environment, there is nothing that anyone can do to mitigate the problem, so it might as well fail further down with an index out of bounds or null pointer exception, it makes no difference.

  3. Nobody should rely on the check being there. (Intentionally allowing the method to be invoked with an invalid argument, catching the resulting IllegalArgumentException, and attempting to take corrective measures is a bad, bad idea.)

As a matter of fact, guaranteeing that a particular exception will be thrown even in production for a condition which you consider to be a bug tempts the n00b programmer to write code which will rely on that exception being thrown in production, and which will, therefore, be a bug in and of itself.

Wrong is also the notion that an assertion indicates a bug 'here'. If the assertion checks a method argument, then the bug that the assertion catches can be at any point along the chain of method calls listed in the stack trace, or it might even be elsewhere.

There are billions of devices out there, most of them running on precious battery power, executing quadrillions of instructions every day, which are just testing against conditions which are guaranteed to never happen by millions of man-hours of testing that has already been painstakingly carried out by developers and testers. This is madness.

So, just plain assert is fine for argument checking. If you ever need to write test code which ensures that your methods do in fact correctly assert against specific erroneous conditions, consider the following construct:

assert index >= 0 : new IllegalArgumentException( "index" );

Obviously, this will only perform the test if assertions are enabled, but the real beauty of it is that if the assertion fails, then the cause of the AssertionError exception will be an IllegalArgumentException, so your testing code will be able to ensure that the correct error was caught.

For more information on this subject, see this article on my blog: michael.gr - Assertions and testing

Milne answered 23/12, 2014 at 14:27 Comment(2)
could you please help me in this related question - #56301162 ? thanks.Miserly
@MasterJoe2 I read your question but I do not have an answer.Milne
S
1

Because assertions are disabled in production builds. If you need to be able to catch when a public method has been used incorrectly, then an assertion would not trigger the check in production builds, and an exception would be a better way to signal errors.

This is especially significant for libraries, because you don't control who and how your methods will be called; for application programs, assertions in public methods are fine as long as and because when you have correct input validation (where "input" may be either user input, or input coming from another system, or from persistent storage), then the assertions should never be triggered.

Sweetie answered 12/12, 2012 at 4:0 Comment(0)
F
0

Use assert to check for problems that should never occur, also in public APIs.

View it as documentation that compiles (and runs, if you want to).

Finagle answered 24/9, 2021 at 11:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.