I'm wondering why the assert keyword is so underused in Java? I've almost never seen them used, but I think they're a great idea. I certainly much prefer the brevity of:
assert param != null : "Param cannot be null";
to the verbosity of:
if (param == null) {
throw new IllegalArgumentException("Param cannot be null");
}
My suspicion is that they're underused because
They arrived relatively late (Java 1.4), by which time many people had already established their Java programming style/habit
They are turned off at runtime by default
assertions are, in theory, for testing invariants, assumptions that must be true in order for the code to complete properly.
The example shown is testing for valid input, which isn't a typical usage for an assertion because it is, generally, user supplied.
Assertions aren't generally used in production code because there is an overhead and it is assumed that situations where the invariants fail have been caught as coding errors during development and testing.
Your point about them coming "late" to java is also a reason why they aren't more widely seen.
Also, unit testing frameworks allow for some of the need for programmatic assertions to be external to the code being tested.
It's an abuse of assertions to use them to test user input. Throwing an IllegalArgumentException on invalid input is more correct, as it allows the calling method to catch the exception, display the error, and do whatever it needs to (ask for input again, quit, whatever).
If that method is a private method inside one of your classes, the assertion is fine, because you are just trying to make sure you aren't accidentally passing it a null argument. You test with assertions on, and when you have tested all the paths through and not triggered the assertion, you can turn them off so that you aren't wasting resources on them. They are also useful just as comments. An assert at the start of a method is good documentation to maintainers that they should be following certain preconditions, and an assert at the end with a postcondition documents what the method should be doing. They can be just as useful as comments; moreso, because with assertions on, they actually TEST what they document.
Assertions are for testing/debugging, not error-checking, which is why they are off by default: to discourage people from using assertions to validate user input.
In "Effective Java", Joshua Bloch suggested (in the "Check parameters for validity" topic) that (sort of like a simple rule to adopt), for public methods, we shall validate the arguments and throw a necessary exception if found invalid, and for non-public methods (which are not exposed and you as the user of them should ensure their validity), we can use assertions instead.
From Programming with Assertions
By default, assertions are disabled at runtime. Two command-line switches allow you to selectively enable or disable assertions.
This means that if you don't have complete control over the run-time environment, you can't guarantee that the assertion code will even be called. Assertions are meant to be used in a test-environment, not for production code. You can't replace exception handling with assertions because if the user runs your application with assertions disabled (the default), all of your error handling code disappears.
#Don, you are frustrated that assertion are turned off by default. I was also, and thus wrote this little javac plugin that inlines them (ie emits the bytecode for if (!expr) throw Ex rather than this silly assert bytecode.
If you include fa.jar in your classpath while compiling Java code, it will do its magic and then tell
Note: %n assertions inlined.
#see http://smallwiki.unibe.ch/adriankuhn/javacompiler/forceassertions and alternatively on github https://github.com/akuhn/javac
I'm not sure why you would bother to write asserts and then replace them with a standard if then condition statement, why not just write the conditions as ifs in the first place?
Asserts are for testing only, and they have two side effects: Larger binaries and degraded performance when enabled (which is why you can turn them off!)
Asserts shouldn't be used to validate conditions because that means the behaviour of your app is different at run time when asserts are enabled/disabled - which is a nightmare!
Assertions are useful because they:
catch PROGRAMMING errors early
document code using code
Think of them as code self-validation. If they fail it should mean that your program is broken and must stop. Always turn them on while unit testing !
In The Pragmatic Programmer they even recommend to let them run in production.
Leave Assertions Turned On
Use Assertions to Prevent the Impossible.
Note that assertions throw AssertionError if they fail, so not caught by catch Exception.
tl;dr
Yes, use assertion-testing in production where it makes sense.
Use other libraries (JUnit, AssertJ, Hamcrest, etc.) rather than the built-in assert facility if you wish.
Most of the other Answers on this page push the maxim "Assertions aren't generally used in production code”. While true in productivity apps such as a word-processor or spreadsheet, in custom business apps where Java is so commonly used, assertion-testing in production is extremely useful, and common.
Like many maxims in the world of programming, what starts out true in one context is misconstrued and then misapplied in other contexts.
Productivity Apps
This maxim of "Assertions aren't generally used in production code”, though common, is incorrect.
Formalized assertion-testing originated with apps such as a word-processor like Microsoft Word or a spreadsheet like Microsoft Excel. These apps might invoke an array of assertion tests assertions on every keystroke made by the user. Such extreme repetition impacted performance severely. So only the beta-versions of such products in limited distribution had assertions enabled. Thus the maxim.
Business Apps
In contrast, in business-oriented apps for data-entry, database, or other data-processing, the use of assertion-testing in production is enormously useful. The insignificant hit on performance makes it quite practical – and common.
Test business rules
Verifying your business rules at runtime in production is entirely reasonable, and should be encouraged. For example:
If an invoice must have one or more line items at all times, then write an assertion testing than the count of invoice line items is greater than zero.
If a product name must be at least 3 characters or more, write an assertion testing the length of the string.
When calculating the balance for a cash ledger, you know the result can never be negative, so run a check for a negative number signaling a flaw in the data or code.
Such tests have no significant impact on performance in production.
Runtime conditions
If your app expects certain conditions to always be true when your app runs in production, write those expectations into your code as assertion tests.
If you expect those conditions may reasonably on occasion fail, then do not write assertion tests. Perhaps throw certain exceptions. Then try to recover where possible.
Sanity-checks
Sanity checks at runtime in production is also entirely reasonable, and should be encouraged. Testing a few arbitrary conditions that one could not imagine being untrue has saved my bacon in countless situations when some bizarre happening occurred.
For example, testing that rounding a nickel (0.05) to the penny resulted in a nickel (0.05) in a certain library helped me in being one of the first people to discover a floating-point technology flaw that Apple shipped in their Rosetta library during the PowerPC-to-Intel transition. Such a flaw reaching the public would have seemed impossible. But amazingly, the flaw had escaped the notice of the originating vendor, Transitive, and Apple, and the early-access developers testing on Apple’s betas.
(By the way, I should mention… never use floating-point for money, use BigDecimal.)
Choice of frameworks
Rather than use the built-in assert facility, you may want to consider using another assertion framework. You have multiple options, including:
JUnitSee: org.junit.jupiter.api.Assertions.
AssertJKnown for its slick fluent interface.
HamcrestUsed across many languages (Java, Python, Ruby, Swift, etc.).
Or roll-your-own. Make a little class to use in your project. Something like this.
package work.basil.example;
public class Assertions {
static public void assertTrue ( Boolean booleanExpression , CharSequence message ) throws java.lang.AssertionError {
if ( booleanExpression ) {
// No code needed here.
} else { // If booleanExpression is false rather than expected true, throw assertion error.
// FIXME: Add logging.
throw new java.lang.AssertionError( message.toString() );
}
}
}
Example usage:
Assertions.assertTrue(
localTime.isBefore( LocalTime.NOON ) ,
"The time-of-day is too late, after noon: " + localTime + ". Message # 816a2a26-2b95-45fa-9b0a-5d10884d819d."
) ;
Your questions
They arrived relatively late (Java 1.4), by which time many people had already established their Java programming style/habit
Yes, this is quite true. Many people were disappointed by the API that Sun/JCP developed for assertion-testing. Its design was lackluster in comparison to existing libraries. So many ignored the new API, and stuck with known tools (3rd-party tools, or roll-your-own mini-library).
They are turned off at runtime by default, WHY OH WHY??
In the earliest years, Java got a bad rap for poor performance speed. Ironically, Java quickly evolved to become one of the best platforms for performance. But the bad rap hung around like a stinky odor. So Sun was extremely wary of anything that might in any measurable way impact performance. So in this perspective, it made sense to make disabling assertion-testing the default.
Another reason to disable by default might have been related to the fact that, in adding the new assertion facility, Sun had hijacked the word assert. This was not a previously reserved keyword, and required one of the few changes ever made to the Java language. The method name assert had been used by many libraries and by many developers in their own code. For some discussion of this historical transition, read this old documentation, Programming With Assertions.
Assertions are very limited: You can only test boolean conditions and you need to write the code for a useful error message every time. Compare this to JUnit's assertEquals() which allows to generate a useful error message from the inputs and even show the two inputs side by side in the IDE in a JUnit runner.
Also, you can't search for assertions in any IDE I've seen so far but every IDE can search for method invocations.
In fact they arrived in Java 1.4.
I think the main problem is that when you code in an environment where you do not manage JVM options directly by yourself like in Eclipse or J2EE servers (in both cases it is possible to change JVM options, but you need to deeply search to find where it can be done), it is easier (I mean it requires less effort) to use if and exceptions (or worse not to use anything).
As others have stated: assertions are not appropriate for validating user input.
If you are concerned with verbosity, I recommend you check out a library I wrote: https://github.com/cowwoc/requirements.java/. It'll allow you to express these checks using very little code, and it'll even generate the error message on your behalf:
requireThat("name", value).isNotNull();
and if you insist on using assertions, you can do this too:
assertThat("name", value).isNotNull();
The output will look like this:
java.lang.NullPointerException: name may not be null
Related
There are many articles explaining why exception flow control should be avoided and is considered an anti-pattern. However, does using exception flow control makes sense in following example?
Eg: I have an enum type with many values and need to check if it contains a given string.
The solution using Enum.valueof shall throw an IllegalArgumentException and can be used to return false. Even Apache commons lang3 lib uses same. We reuse one time lazy initialized enumConstantDirectory map on all subsequent calls.
return EnumUtils.isValidEnum(MyEnum.class, myValue);
Other solution would be to iterate.
return Stream.of(MyEnum.values()).anyMatch(e -> e.name().equals(value));
Programming is always about balancing between "informal standards" and deviating from rules where it makes sense.
Yes, Java is one of those languages where you "look before you leap" (you check conditions); not like Python where you "ask for forgiveness" (not for permission).
In other words: most of the time, a Java programmer would find it surprising to look at code that uses exceptions for control flows. But: that doesn't mean that one never should work this way.
My personal two cent here: I think you should rely on exceptions when that case indicates a real exceptional condition in your program. Meaning: when those strings you are checking are supposed to represent enum constants ... than an exception might be fine. But when there is a certain chance (and maybe even "valid" reasons) at runtime that a string is invalid, then I would rather use a method that goes boolean isValid() and not throw an exception.
To do internal logic checks, there are two ways in Java,
use the assert keyword: e.g, assert(x>y);
manually throw assertion error: e.g, if(y>x) throw new AssertionError();
What are the differences among above two methods( performance wise, programming flexibility, etc.?
Which one is considered as a good programming practice?
The main difference is that assert is not guaranteed to be processed, unless assertions are explicitly enabled (either via the -ea option to java, or programmatically). On the other hand, throwing a new AssertionError() will always work.
Some reading information: Programming with Assertions
Way #1 "assert(x>y);" uses a JVM feature which is turned off by default. However, it gives you more flexibility since you can turn it on and off as you like with one single parameter.
Way #2 "if(y>x) throw new AssertionError();" will always be executed, you can't turn it off via the assert-param. It is just an exception.
I've often seen people use Exceptions for "real" errors (network not available, wrong input provided), while assertions are often used (i.e. turned on) during development/integration for very basic checks (e.g. param not null). IMO, it's hard to draw the line.
When assert(x>y) fails, then it would raise AssertionError(provided assertions are enabled). The advantage of using java assertions is that it can be enabled or disabled during compile time. You can disable assertions for production environment which improves performance compared to raising manually AssertionError in which you always perform assertions thereby reducing performance.
Non of above answers answer my second question (Which one is considered as a good programming practice?). So I talked regards this with one of my friend and according to him, If some thing is private to the package and you are the only one calling that function (e.g, private functions) then use assert. If something is public and you expect some other third party developer may directly call the function, then do a explicit if check and throw the exception.
Most of the time I will use an exception to check for a condition in my code, I wonder when it is an appropriate time to use an assertion?
For instance,
Group group=null;
try{
group = service().getGroup("abc");
}catch(Exception e){
//I dont log error because I know whenever error occur mean group not found
}
if(group !=null)
{
//do something
}
Could you indicate how an assertion fits in here? Should I use an assertion?
It seems like I never use assertions in production code and only see assertions in unit tests. I do know that in most cases, I can just use exception to do the checking like above, but I want to know appropriate way to do it "professionally".
Out of my mind (list may be incomplete, and is too long to fit in a comment), I would say:
use exceptions when checking parameters passed to public or protected methods and constructors
use exceptions when interacting with the user or when you expect the client code to recover from an exceptional situation
use exceptions to address problems that might occur
use assertions when checking pre-conditions, post-conditions and invariants of private/internal code
use assertions to provide feedback to yourself or your developer team
use assertions when checking for things that are very unlikely to happen otherwise it means that there is a serious flaw in your application
use assertions to state things that you (supposedly) know to be true
In other words, exceptions address the robustness of your application while assertions address its correctness.
Assertions are designed to be cheap to write, you can use them almost everywhere and I'm using this rule of thumb: the more an assertion statement looks stupid, the more valuable it is and the more information it embeds. When debugging a program that does not behave the right way, you will surely check the more obvious failure possibilities based on your experience. Then you will check for problems that just cannot happen: this is exactly when assertions help a lot and save time.
Assertions should be used to check something that should never happen, while an exception should be used to check something that might happen.
For example, a function might divide by 0, so an exception should be used, but an assertion could be used to check that the harddrive suddenly disappears.
An assertion would stop the program from running, but an exception would let the program continue running.
Note that if(group != null) is not an assertion, that is just a conditional.
Remember assertions can be disabled at runtime using parameters, and are disabled by default, so don't count on them except for debugging purposes.
Also you should read the Oracle article about assert to see more cases where to use - or not to use - assert.
As a general rule:
Use assertions for internal consistency checks where it doesn't matter at all if someone turns them off. (Note that the java command turns off all assertions by default.)
Use regular tests for any kind of checks what shouldn't be turned off. This includes defensive checks that guard against potential damage cause by bugs, and any validation data / requests / whatever provided by users or external services.
The following code from your question is bad style and potentially buggy
try {
group = service().getGroup("abc");
} catch (Exception e) {
//i dont log error because i know whenever error occur mean group not found
}
The problem is that you DON'T know that an exception means that the group was not found. It is also possible that the service() call threw an exception, or that it returned null which then caused a NullPointerException.
When you catch an "expected" exception, you should catch only the exception that you are expecting. By catching java.lang.Exception (and especially by not logging it), you are making it harder to diagnose / debug the problem, and potentially allowing the app to do more damage.
Well, back at Microsoft, the recommendation was to throw Exceptions in all APIs you make available publicly and use Asserts in all sorts of assumptions you make about code that's internal. It's a bit of a loose definition but I guess it's up to each developer to draw the line.
Regarding the use of Exceptions, as the name says, their usage should be exceptional so for the code you present above, the getGroup call should return null if no service exists. Exception should only occur if a network link goes down or something like that.
I guess the conclusion is that it's a bit left down to the development team for each application to define the boundaries of assert vs exceptions.
According to this doc http://docs.oracle.com/javase/6/docs/technotes/guides/language/assert.html#design-faq-general, "The assert statement is appropriate for nonpublic precondition, postcondition and class invariant checking. Public precondition checking should still be performed by checks inside methods that result in particular, documented exceptions, such as IllegalArgumentException and IllegalStateException."
If you want to know more about precondition, postcondition and class invariant, check this doc: http://docs.oracle.com/javase/6/docs/technotes/guides/language/assert.html#usage-conditions. It also contains with examples of assertions usage.
Testing for null will only catch nulls causing problems, whereas a try/catch as you have it will catch any error.
Broadly, try/catch is safer, but slightly slower, and you have to be careful that you catch all the kinds of error that may occur. So I would say use try/catch - one day the getGroup code may change, and you just might need that bigger net.
You can use this simple difference in mind while their usage. Exceptions will be used for checking expected and unexpected errors called checked and unchecked error while assertion is used mainly for debugging purposes at the run time to see whether the assumptions are validated or not.
Unfortunately asserts can be disabled. When in production you need all the help you can get when tracking down something unforeseen, so asserts disqualify themselves.
I confess I'm a little confused by your question. When an assertion condition is not met, an exception is thrown. Confusingly this is called AssertionError. Note that it's unchecked, like (for example) IllegalArgumentException which is thrown in very similar circumstances.
So using assertions in Java
is a more concise means of writing a condition/throw block
permits you to turn these checks on/off via JVM parameters. Normally I would leave these checks on all the time, unless they impact runtime performance or have a similar penalty.
See section 6.1.2 (Assertions vs. other error code) of Sun's documentation at the following link.
http://www.oracle.com/technetwork/articles/javase/javapch06.pdf
This document gives the best advice I've seen on when to use assertions. Quoting from the document:
"A good rule of thumb is that you should use an assertion for exceptional cases that you would like to forget about. An assertion is the quickest way to deal with, and forget, a condition or state that you don’t expect to have to deal with."
I’m not experienced with Java asserts but I think the answer here naturally follows from what Java asserts are:
Code that is only run if enabled
Disabled by default
Thus we can surmise that assertions should never be used to implement application logic: it should only be used as optional (because of the on/off feature) sanity checks/explicit assumptions. The correctness of your app (modulo bugs!) cannot depend on a runtime flag which is turned off by default.
So only use assertions to check internal assumptions (your own code only, not things that are out of your control like user input). But on the other hand you might sometimes want to use exceptions to check your assumptions; if you want to always check a certain assumption then you don’t need the on/off feature that assertions provide.
For the task of checking assumptions, it seems that assertions excel when:
The check might be costly so you want the option of disabling it
Using if-or-throw would be too much “noise” but you can bear the cost of one little assert <expr>
You want to leave a little comment which merely states something like:
// NOTE we KNOW that `x` is not null!
x.doWork();
Then the Java folks say that you should always replace that line with:
assert x != null;
x.doWork();
Or (in my opinion) if you prefer:
if (x == null) {
throw new IllegalStateException("shouldn’t be null");
}
x.doWork();
Things that follow from these principles
I think we can immediately expand on related principles (sibling answer) by using this “optional” principle (my gloss in cursive):
use exceptions when checking parameters passed to public or protected methods and constructors
Because you are checking things outside your code: code coming into your module
use exceptions when interacting with the user or when you expect the client code to recover from an exceptional situation
Because the input is from the user and has nothing to do with your code. Also assertions are not really something you are supposed to recover from.(†1)
use exceptions to address problems that might occur
I.e. errors that happen as a matter of course: not bugs in your code
use assertions when checking pre-conditions, post-conditions and invariants of private/internal code
Because it’s your own code: problems inside your own code are self-caused
use assertions to provide feedback to yourself or your developer team
Bugs are for developers to deal with
use assertions when checking for things that are very unlikely to happen otherwise it means that there is a serious flaw in your application
Key word “serious flaw in your application”
use assertions to state things that you (supposedly) know to be true
Checking assumptions
Notes
I think this is a general opinion. See also (my bold):
Why is AssertionError a subclass of Error rather than RuntimeException?
This issue was controversial. The expert group discussed it at length, and came to the conclusion that Error was more appropriate to discourage programmers from attempting to recover from assertion failures.
Is it a good practice to use Assert for function parameters to enforce their validity. I was going through the source code of Spring Framework and I noticed that they use Assert.notNull a lot. Here's an example
public static ParsedSql parseSqlStatement(String sql) {
Assert.notNull(sql, "SQL must not be null");
}
Here's Another one:
public NamedParameterJdbcTemplate(DataSource dataSource) {
Assert.notNull(dataSource,
"The [dataSource] argument cannot be null.");
this.classicJdbcTemplate = new JdbcTemplate(dataSource);
}
public NamedParameterJdbcTemplate(JdbcOperations classicJdbcTemplate) {
Assert.notNull(classicJdbcTemplate,
"JdbcTemplate must not be null");
this.classicJdbcTemplate = classicJdbcTemplate;
}
FYI, The Assert.notNull (not the assert statement) is defined in a util class as follows:
public abstract class Assert {
public static void notNull(Object object, String message) {
if (object == null) {
throw new IllegalArgumentException (message);
}
}
}
In principle, assertions are not that different from many other run-time checkings.
For example, Java bound-checks all array accesses at run-time. Does this make things a bit slower? Yes. Is it beneficial? Absolutely! As soon as out-of-bound violation occurs, an exception is thrown and the programmer is alerted to any possible bug! The behavior in other systems where array accesses are not bound-checked are A LOT MORE UNPREDICTABLE! (often with disastrous consequences!).
Assertions, whether you use library or language support, is similar in spirit. There are performance costs, but it's absolutely worth it. In fact, assertions are even more valuable because it's explicit, and it communicates higher-level concepts.
Used properly, the performance cost can be minimized and the value, both for the client (who will catch contract violations sooner rather than later) and the developers (because the contract is self-enforcing and self-documenting), is maximized.
Another way to look at it is to think of assertions as "active comments". There's no arguing that comments are useful, but they're PASSIVE; computationally they do nothing. By formulating some concepts as assertions instead of comments, they become ACTIVE. They actually must hold at run time; violations will be caught.
See also: the benefits of programming with assertions
Those asserts are library-supplied and are not the same as the built-in assert keyword.
There's a difference here: asserts do not run by default (they must be enabled with the -ea parameter), while the assertions provided by the Assert class cannot be disabled.
In my opinion (for what it's worth), this is as good a method as any for validating parameters. If you had used built-in assertions as the question title implies, I would have argued against it on the basis that necessary checks should not be removable. But this way is just shorthand for:
public static ParsedSql parseSqlStatement(String sql) {
if (sql == null)
throw new IllegalArgumentException("SQL must not be null");
...
}
... which is always good practice to do in public methods.
The built-in style of asserts is more useful for situations where a condition should always be true, or for private methods. The language guide introducing assertions has some good guidelines which are basically what I've just described.
Yes it is good practice.
In the Spring case, it is particularly important because the checks are validating property settings, etc that are typically coming from XML wiring files. In other words, they are validating the webapp's configuration. And if you ever do any serious Spring-based development, those validation checks will save you hours of debugging when you make a silly configuration mistake.
But note that there is a BIG difference between a library class called Assert and the Java assert keyword which is used to define a Java assertion. The latter form of assertions can be turned off at application launch time, and should NOT be used for argument validation checks that you always want to happen. Clearly, the Spring designers think it would be a really bad idea to turn off webapp configuration sanity checks ... and I agree.
UPDATE
In Java 7 (and later) the java.util.Objects class provides a requireNonNull convenience method to test if an argument is null and raise an exception. You use it like this:
SomeType t = ...
SomeType tChecked = Objects.requireNonNull(t);
or
SomeType tChecked = Objects.requireNonNull(t, "t should be non-null");
However, note that this method raises NullPointerException rather than IllegalArgumentException.
Based on Sun's guide on assertions, you should not use assertions for argument checking in public methods.
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.
In very large and poorly designed/maintained systems, if you're looking to improve predictability in methods that are, say, 6000 lines long and nobody in the company understands them anymore, it can be valuable to use the assert keyword to cause development environments to blow up, revealing bugs. But were you to implement those assertions in production, you might shortcircuit a patch that, though horribly conceived, fixed a problem. You want to fix that bad patch by discovering it in the dev environment, not production. So you would turn asserts on at development time, and turn them off in production.
Another valid use of the assert keyword at development time is to insert validity checks into algorithms that must execute in sub-millisecond times and are well enough insulated from unpredictable or untested callers. You may not be able to afford to preserve the validity check in production in such a case, though it's still very useful in development. On the other hand, if the source of the parameters you're validating is unpredictable or could become so (if it's determined partly by user input, for example), you can probably never afford to skip the check, even in production, and should take the performance hit as a cost of doing business. (In this last case, you probably wouldn't want to use an assert.) But you should opt for asserts to eliminate a production-time validity check only after profiling tells you you simply can't afford the overhead.
Yes it's a good idea. You're enforcing the contracting of the interface or class. If there is a contract violation you want to detect it as soon as possible. The longer you wait the more unpredictable the results can be and the harder it can be to diagnose.
When you explicitly check like this you should also provide an information message that when viewed in a log file can give useful context to help find the root cause or even just to realize you've made a wrong assumption about what the contract is.
I'm keeping my assertions in released binaries but with modified behavior: abort is not called but stacktrace is collected.
More details here: http://blog.aplikacja.info/2011/10/assert-to-abort-or-not-to-abort-thats-the-question/
Finally, I have a question to ask on Stack Overflow! :-)
The main target is for Java but I believe it is mostly language agnostic: if you don't have native assert, you can always simulate it.
I work for a company selling a suite of softwares written in Java. The code is old, dating back to Java 1.3 at least, and at some places, it shows... That's a large code base, some 2 millions of lines, so we can't refactor it all at once.
Recently, we switched the latest versions from Java 1.4 syntax and JVM to Java 1.6, making conservative use of some new features like assert (we used to use a DEBUG.ASSERT macro -- I know assert has been introduced in 1.4 but we didn't used it before), generics (only typed collections), foreach loop, enums, etc.
I am still a bit green about the use of assert, although I have read a couple of articles on the topic. Yet, some usages I see leave me perplex, hurting my common sense... ^_^ So I thought I should ask some questions, to see if I am right to want to correct stuff, or if it goes against common practices. I am wordy, so I bolded the questions, for those liking to skim stuff.
For reference, I have searched assert java in SO and found some interesting threads, but apparently no exact duplicate.
How to avoid “!= null” statements in java? and How much null checking is enough? are quite relevant, because lot of asserts we have just check if variable is null. At some places in our code, there are usages of the null object (eg. returning new String[0]) but not always. We have to live with that, at least for maintenance of legacy code.
Some good answers also in Java assertions underused.
Oh, and SO indicates with reason that When should I use Debug.Assert()? question is related too (nice feature to reduce duplicates!).
First, main issue, which triggered my question today:
SubDocument aSubDoc = documents.GetAt( i );
assert( aSubDoc != null );
if ( aSubDoc.GetType() == GIS_DOC )
{
continue;
}
assert( aSubDoc.GetDoc() != null );
ContentsInfo ci = (ContentsInfo) aSubDoc.GetDoc();
(Yes, we use MS' C/C++ style/code conventions. And I even like it (coming from same background)! So sue us.)
First, the assert() form comes from conversion of DEBUG.ASSERT() calls. I dislike the extra parentheses, since assert is a language construct, not (no longer, here) a function call. I dislike also return (foo); :-)
Next, the asserts don't test here for invariants, they are rather used as guards against bad values. But as I understand it, they are useless here: the assert will throw an exception, not even documented with a companion string, and only if assertions are enabled. So if we have -ea option, we just have an assertion thrown instead of the regular NullPointerException one. That doesn't look like a paramount advantage, since we catch unchecked exceptions at highest level anyway.
Am I right supposing we can get rid of them and live with that (ie. let Java raise such unckecked exception)? (or, of course, test against null value if likely, which is done in other places).
Side note: should I have to assert in the above snippet, I would do that against ci value, not against the getter: even if most getters are optimized/inlined, we cannot be sure, so we should avoid calling it twice.
Somebody told, in the last referenced thread, that public methods should use tests against values of parameters (usage of the public API) and private methods should rely on asserts instead. Good advice.
Now, both kinds of methods must check another source of data: external input. Ie. data coming from user, from a database, from some file or from the network, for example.
In our code, I see asserts against these values. I always change these to real test, so they act even with assertions disabled: these are not invariants and must be properly handled.
I see only one possible exception, where input is supposed constant, for example a database table filled with constants used in relations: program would break if this table is changed but corresponding code wasn't updated.
Do you see other exceptions?
Another relatively frequent use I see, which seems OK: in the default of a switch, or at the end of a series of else if testing all possible values (these cases date back before our use of enums!), there is often an assert false : "Unexpected value for stuff: " + stuff;
Looks legitimate for me (these cases shouldn't happen in production), what do you think? (beyond the "no switch, use OO" advices which are irrelevant here).
And finally, are there any other useful use cases or annoying gotchas I missed here? (probably!)
The number one rule is to avoid side-effects in assertions. In other words, the code should behave identically with assertions turned off as it does when assertions are turned on and not failing (obviously assertions that fail are going to alter the behaviour because they will raise an error).
The number two rule is not to use assertions for essential checks. They can be turned off (or, more correctly, not turned on). For parameter-checking of non-private methods use IllegalArgumentException.
Assertions are executable assumptions. I use assertions to state my beliefs about the current state of the program. For example, things like "I assume that n is positive here", or "I assume that the list has precisely one element here".
I use assert, not only for parameter validation, but also used for verifying Threads.
Every time I do swing, I write assert in almost every method to mark "I should only be execute in worker thread/AWTThread". (I think Sun should do it for us.) Because of the Swing threading model, it MAY NOT fail (and randomly fail) if you access swing api from non-UI thread. It is quite difficult to find out all these problem without assert.
Another example I can imagination is to check JAR enclosed resource. You can have english exception rather then NPE.
EDIT:
Another example; object lock checking. If I know that I am going to use nested synchronized block, or when I am going to fix a deadlock, I use Thread.holdLock(Object) to ensure I won't get the locks in reverse order.
EDIT(2): If you are quite sure some code block should never be reach, you may write
throw new AssertionError("You dead");
rather then
assert false:"I am lucky";
For example, if you override "equals(Object)" on a mutable object, override hashCode() with AssertionError if you believe it will never be the key. This practice is suggested in some books. I won't hurt performance (as it should never reach).
you have touched on many of the reasons why i think asserts should be avoided in general. unless you are working with a codebase where assert usage has very strict guidelines, you very quickly get into a situation where you cannot ever turn the assertions off, in which case you might as well just be using normal logic tests.
so, my recommendation is skip the assertions. don't stick in extra null-pointer checks where the language will do it for you. however, if the pointer may not be dereferenced for a while, up-front null checking is a good idea. also, always use real exceptions for cases which should "never" happen (the final if branch or the default switch case), don't use "assert false". if you use an assertion, there's a chance someone could turn it off, and if the situation actually happens, things will get really confused.
I recommend checking parameters in public (API) methods and throwing IllegalArgumentException if the params aren't valid. No asserts here as an API user requires to get a proper error (message).
Asserts should be used in non-public methods to check post-conditions and possibly pre-conditions. For example:
List returnListOfSize(int size) {
// complex list creation
assert list.size == size;
}
Often using a clever error handling strategy asserts can be circumvented.