Why shouldn't statements be used to test arguments in public methods?

Well, I never worked with statements in my limited experience with java and wondered why I read on many sites and many books that deal with statements, the same warning that assert statements should not be used to test arguments in public methods ? I was wondering if this is related to the execution order of the assert statement regarding other statements in java.

+7
source share
7 answers

The purpose of the statements is to check your program logic - the statement error is "Stop everything - there is an error!". indication. In particular, an assertion error indicates “there is an error HERE”, but “HERE” is somewhere inside your code, and the reason for the failure can only be really determined by examining your code (which the user of your API cannot and should not expect).

When you receive bad data through the API, you want to indicate "Hey, you gave me bad data!" IllegalArgumentException and its relatives are a way to indicate this.

(But note that there is nothing wrong with using assert checks on the WITHIN parameters of your code - where you do not support the truly "open" API that people outside your team will use.)

But this raises another point: as far as it is reasonable / possible, you should "catch" the IllegalArgumentException ilk internal exceptions that may arise due to your own errors and convert them to FatalError or some of them, so the user of your API does not cause search for a bad parameter on its part when there is an error in your code.

(Also note the difference between public - the Java keyword and a "public interface" - which means some interface that becomes available as a "formal" API that will be used by people outside your development team. This is the last case we are worried about here .)

+3
source

Informally, argument checking and assertions are used for different purposes:

  • Checking the arguments consists in detecting situations when the person calling your method does something wrong, and
  • Statements are used to detect situations when you are doing something wrong.

Essentially, when you state a condition

 assert val < total; 

The check conveys the following simple English thoughts to the readers of your code: "I checked my code and, according to my reasoning, I am sure that val will always be less than total ."

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

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

your code says “the caller forgot to make sure that val less than total ”.

These are two different thoughts, so it’s natural to use two different ways to convey them in your code.

+4
source

By programming with statements

Validation of arguments is usually part of the published specifications (or contract) of the method, and these specifications must be satisfied if statements are enabled or disabled. Another problem with using statements to verify arguments is that erroneous arguments should result in a corresponding runtime exception (e.g. IllegalArgumentException, IndexOutOfBoundsException or NullPointerException). An assertion error will not result in a corresponding exception .

+3
source

First, Java statements are deleted at runtime if they are not explicitly included at compilation time.

Exceptions are more suitable for checking parameters, because you expect to process them, while the statement has the semantic meaning "it MUST be true at the moment in the code, otherwise I don’t know how to handle it."

+2
source

Because statements are disabled in the assembly. If you need to catch when the public method was used incorrectly, then the statement will not trigger a check in the production assembly, and an exception will be the best way to signal errors.

This is especially important for libraries because you do not control who and how your methods will be called; for applications, statements in public methods are accurate as long as you have a valid input check (where "enter" can be either an input user, or an input from another system, or from persistent storage), then statements never should be launched.

+1
source

The notion that statements should not be used to test arguments in public methods is simply not true. Just because you are reading something in a book does not mean that it is correct.

Checking arguments in public methods falls directly into the general category of checking for errors, so it should be considered as such, and the mechanism that we already have for detecting errors is statements.

If the public interface of a method says that the argument "index" for this method should never be negative ", then calling it with a negative index is an error, and the following things are saved:

  • This should not occur in a production environment. (Testing should guarantee this.)

  • Even if this happens in a production environment, nothing can be done to mitigate the problem, so it may also not work with an index outside or an exception using a null pointer, it makes no difference.

  • No one should rely on verification. (Intentionally using a method with an invalid argument, catching an IllegalArgumentException, and trying to take corrective measures is a bad and bad idea.)

In fact, ensuring that a particular exception is thrown even in production for a condition that you consider to be an error, it forces the n00b programmer to write code that will rely on the exception to be thrown into production, and which will therefore be itself a mistake.

The notion that the statement indicates an error "here" is also incorrect. If the statement checks the argument of the method, then the error that the statement catches can be at any point along the chain of method calls listed in the stack trace, or it can even be in another place.

There are billions of devices, most of which run on a priceless battery, execute quadrillion instructions every day, which simply check the conditions, which are guaranteed to never happen millions of man-hours of testing, which have already been carefully done by developers and testers. This is madness.

So, just a simple assert great for checking arguments. If you ever need to write test code that ensures that your methods truly validate against certain erroneous conditions, consider the following construction:

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

Obviously, this will only perform those tests if the statements are included, but the real beauty is that if the statement fails, then the cause the AssertionError exception will be an IllegalArgumentException , so your testing code can ensure that the correct error was detected.

For more information on this, see this blog post: michael.gr - Approvals and Testing

0
source
Mike gave an excellent answer, which, unfortunately, is rarely defended in the Java literature. I'm sorry that I do not have enough reputation to support this. Instead, I will give my own answer, which I hope will add additional support to Mike's views.

The vast majority of the Java literature spreads the dogma that you should not use assert to check the public arguments of a method. In other words, they say that assert should not be used to validate prerequisites for public methods and that instead you should use explicit if (!precond) throw SomeException(); . The Java standard library is full of examples of this policy.

Arguments confirming this are as follows:

  • Responsibility for compliance with the prerequisites is a function call (customer code), and not your code (vendor code).
  • Validation of claims is optional, so it is best to validate validation.

Well, that seems like a very protective attitude towards me. The clients of your code are programmers, just like you. Satisfying preconditions is the responsibility of the customers, of course, and if the customers do not, this is a mistake; customer error, not yours. Of course, you expect your customers to test their programs with claims included, right? But once they are convinced of the correctness of their program, why should you still impose your useless exceptions?

Now let's look at it from the point of view of the client. You are using String.charAt . The documentation for this method reports that

public char charAt (int index)

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

The precondition is clearly indicated. But later he adds

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

So, if you know for sure that your index is within the bounds, you still put your call inside try ... catch ? Note that the documentation does not really tell you that an exception will not be thrown if you respect the precondition, but of course, what do you expect, right?

So, you are sure that the index is within the bounds, you might even have argued that you didn’t waste time and space on a useless try , which will never catch anything, but String will still spend time checking that you behave well.

As i see him

  • Claims should be used to verify conditions that are entirely under the control of the programmer, whether it be a client or a provider.
  • Explicit conditional instructions with exceptions (or another error signaling device, such as an error return code) should be used to verify conditions that depend on uncontrolled external factors, such as user input, unpredictable operating system restrictions, etc.

This is what I teach my students. I do not tell them. This is not a dogma. I let them know that this is contrary to what they will read in most books, but I urge them to decide for themselves.

This question does not apply to Java; it is a question of programming methodology. The approach I follow is akin to Design by Contract . Some languages ​​have special syntax to support this style, for example, the ability to explicitly declare preconditions, postconditions, and object invariants, and they are explicitly included as part of the public specification (and documentation) of the code.

Joao Rodriguez

0
source

All Articles