What does the Java assert keyword do, and when should it be used?

橙三吉。 提交于 2019-12-16 20:04:06

问题


What are some real life examples to understand the key role of assertions?


回答1:


Assertions (by way of the assert keyword) were added in Java 1.4. They are used to verify the correctness of an invariant in the code. They should never be triggered in production code, and are indicative of a bug or misuse of a code path. They can be activated at run-time by way of the -ea option on the java command, but are not turned on by default.

An example:

public Foo acquireFoo(int id) {
  Foo result = null;
  if (id > 50) {
    result = fooService.read(id);
  } else {
    result = new Foo(id);
  }
  assert result != null;

  return result;
}



回答2:


Let's assume that you are supposed to write a program to control a nuclear power-plant. It is pretty obvious that even the most minor mistake could have catastrophic results, therefore your code has to be bug-free (assuming that the JVM is bug-free for the sake of the argument).

Java is not a verifiable language, which means: you cannot calculate that the result of your operation will be perfect. The main reason for this are pointers: they can point anywhere or nowhere, therefore they cannot be calculated to be of this exact value, at least not within a reasonable span of code. Given this problem, there is no way to prove that your code is correct at a whole. But what you can do is to prove that you at least find every bug when it happens.

This idea is based on the Design-by-Contract (DbC) paradigm: you first define (with mathematical precision) what your method is supposed to do, and then verify this by testing it during actual execution. Example:

// Calculates the sum of a (int) + b (int) and returns the result (int).
int sum(int a, int b) {
  return a + b;
}

While this is pretty obvious to work fine, most programmers will not see the hidden bug inside this one (hint: the Ariane V crashed because of a similar bug). Now DbC defines that you must always check the input and output of a function to verify that it worked correctly. Java can do this through assertions:

// Calculates the sum of a (int) + b (int) and returns the result (int).
int sum(int a, int b) {
    assert (Integer.MAX_VALUE - a >= b) : "Value of " + a + " + " + b + " is too large to add.";
  final int result = a + b;
    assert (result - a == b) : "Sum of " + a + " + " + b + " returned wrong sum " + result;
  return result;
}

Should this function now ever fail, you will notice it. You will know that there is a problem in your code, you know where it is and you know what caused it (similar to Exceptions). And what is even more important: you stop executing right when it happens to prevent any further code to work with wrong values and potentially cause damage to whatever it controls.

Java Exceptions are a similar concept, but they fail to verify everything. If you want even more checks (at the cost of execution speed) you need to use assertions. Doing so will bloat your code, but you can in the end deliver a product at a surprisingly short development time (the earlier you fix a bug, the lower the cost). And in addition: if there is any bug inside your code, you will detect it. There is no way of a bug slipping-through and cause issues later.

This still is not a guarantee for bug-free code, but it is much closer to that, than usual programs.




回答3:


Assertions are a development-phase tool to catch bugs in your code. They're designed to be easily removed, so they won't exist in production code. So assertions are not part of the "solution" that you deliver to the customer. They're internal checks to make sure that the assumptions you're making are correct. The most common example is to test for null. Many methods are written like this:

void doSomething(Widget widget) {
  if (widget != null) {
    widget.someMethod(); // ...
    ... // do more stuff with this widget
  }
}

Very often in a method like this, the widget should simply never be null. So if it's null, there's a bug in your code somewhere that you need to track down. But the code above will never tell you this. So in a well-intentioned effort to write "safe" code, you're also hiding a bug. It's much better to write code like this:

/**
 * @param Widget widget Should never be null
 */
void doSomething(Widget widget) {
  assert widget != null;
  widget.someMethod(); // ...
    ... // do more stuff with this widget
}

This way, you will be sure to catch this bug early. (It's also useful to specify in the contract that this parameter should never be null.) Be sure to turn assertions on when you test your code during development. (And persuading your colleagues to do this, too is often difficult, which I find very annoying.)

Now, some of your colleagues will object to this code, arguing that you should still put in the null check to prevent an exception in production. In that case, the assertion is still useful. You can write it like this:

void doSomething(Widget widget) {
  assert widget != null;
  if (widget != null) {
    widget.someMethod(); // ...
    ... // do more stuff with this widget
  }
}

This way, your colleagues will be happy that the null check is there for production code, but during development, you're no longer hiding the bug when widget is null.

Here's a real-world example: I once wrote a method that compared two arbitrary values for equality, where either value could be null:

/**
 * Compare two values using equals(), after checking for null.
 * @param thisValue (may be null)
 * @param otherValue (may be null)
 * @return True if they are both null or if equals() returns true
 */
public static boolean compare(final Object thisValue, final Object otherValue) {
  boolean result;
  if (thisValue == null) {
    result = otherValue == null;
  } else {
    result = thisValue.equals(otherValue);
  }
  return result;
}

This code delegates the work of the equals() method in the case where thisValue is not null. But it assumes the equals() method correctly fulfills the contract of equals() by properly handling a null parameter.

A colleague objected to my code, telling me that many of our classes have buggy equals() methods that don't test for null, so I should put that check into this method. It's debatable if this is wise, or if we should force the error, so we can spot it and fix it, but I deferred to my colleague and put in a null check, which I've marked with a comment:

public static boolean compare(final Object thisValue, final Object otherValue) {
  boolean result;
  if (thisValue == null) {
    result = otherValue == null;
  } else {
    result = otherValue != null && thisValue.equals(otherValue); // questionable null check
  }
  return result;
}

The additional check here, other != null, is only necessary if the equals() method fails to check for null as required by its contract.

Rather than engage in a fruitless debate with my colleague about the wisdom of letting the buggy code stay in our code base, I simply put two assertions in the code. These assertions will let me know, during the development phase, if one of our classes fails to implement equals() properly, so I can fix it:

public static boolean compare(final Object thisValue, final Object otherValue) {
  boolean result;
  if (thisValue == null) {
    result = otherValue == null;
    assert otherValue == null || otherValue.equals(null) == false;
  } else {
    result = otherValue != null && thisValue.equals(otherValue);
    assert thisValue.equals(null) == false;
  }
  return result;
}

The important points to keep in mind are these:

  1. Assertions are development-phase tools only.

  2. The point of an assertion is to let you know if there's a bug, not just in your code, but in your code base. (The assertions here will actually flag bugs in other classes.)

  3. Even if my colleague was confident that our classes were properly written, the assertions here would still be useful. New classes will be added that might fail to test for null, and this method can flag those bugs for us.

  4. In development, you should always turn assertions on, even if the code you've written doesn't use assertions. My IDE is set to always do this by default for any new executable.

  5. The assertions don't change the behavior of the code in production, so my colleague is happy that the null check is there, and that this method will execute properly even if the equals() method is buggy. I'm happy because I will catch any buggy equals() method in development.

Also, you should test your assertion policy by putting in a temporary assertion that will fail, so you can be certain that you are notified, either through the log file or a stack trace in the output stream.




回答4:


A lot of good answers explaining what the assert keyword does, but few answering the real question, "when should the assert keyword be used in real life?"

The answer: almost never.

Assertions, as a concept, are wonderful. Good code has lots of if (...) throw ... statements (and their relatives like Objects.requireNonNull and Math.addExact). However, certain design decisions have greatly limited the utility of the assert keyword itself.

The driving idea behind the assert keyword is premature optimization, and the main feature is being able to easily turn off all checks. In fact, the assert checks are turned off by default.

However, it is critically important that invariant checks continue to be done in production. This is because perfect test coverage is impossible, and all production code will have bugs which assertions should help to diagnose and mitigate.

Therefore, the use of if (...) throw ... should be preferred, just as it is required for checking parameter values of public methods and for throwing IllegalArgumentException.

Occasionally, one might be tempted to write an invariant check that does take an undesirably long time to process (and is called often enough for it to matter). However, such checks will slow down testing which is also undesirable. Such time-consuming checks are usually written as unit tests. Nevertheless, it may sometimes make sense to use assert for this reason.

Do not use assert simply because it is cleaner and prettier than if (...) throw ... (and I say that with great pain, because I like clean and pretty). If you just cannot help yourself, and can control how your application is launched, then feel free to use assert but always enable assertions in production. Admittedly, this is what I tend to do. I am pushing for a lombok annotation that will cause assert to act more like if (...) throw .... Vote for it here.

(Rant: the JVM devs were a bunch of awful, prematurely optimizing coders. That is why you hear about so many security issues in the Java plugin and JVM. They refused to include basic checks and assertions in production code, and we are continuing to pay the price.)




回答5:


Here's the most common use case. Suppose you're switching on an enum value:

switch (fruit) {
  case apple:
    // do something
    break;
  case pear:
    // do something
    break;
  case banana:
    // do something
    break;
}

As long as you handle every case, you're fine. But someday, somebody will add fig to your enum and forget to add it to your switch statement. This produces a bug that may get tricky to catch, because the effects won't be felt until after you've left the switch statement. But if you write your switch like this, you can catch it immediately:

switch (fruit) {
  case apple:
    // do something
    break;
  case pear:
    // do something
    break;
  case banana:
    // do something
    break;
  default:
    assert false : "Missing enum value: " + fruit;
}



回答6:


Assertions are used to check post-conditions and "should never fail" pre-conditions. Correct code should never fail an assertion; when they trigger, they should indicate a bug (hopefully at a place that is close to where the actual locus of the problem is).

An example of an assertion might be to check that a particular group of methods is called in the right order (e.g., that hasNext() is called before next() in an Iterator).




回答7:


What does the assert keyword in Java do?

Let's look at the compiled bytecode.

We will conclude that:

public class Assert {
    public static void main(String[] args) {
        assert System.currentTimeMillis() == 0L;
    }
}

generates almost the exact same bytecode as:

public class Assert {
    static final boolean $assertionsDisabled =
        !Assert.class.desiredAssertionStatus();
    public static void main(String[] args) {
        if (!$assertionsDisabled) {
            if (System.currentTimeMillis() != 0L) {
                throw new AssertionError();
            }
        }
    }
}

where Assert.class.desiredAssertionStatus() is true when -ea is passed on the command line, and false otherwise.

We use System.currentTimeMillis() to ensure that it won't get optimized away (assert true; did).

The synthetic field is generated so that Java only needs to call Assert.class.desiredAssertionStatus() once at load time, and it then caches the result there. See also: What is the meaning of "static synthetic"?

We can verify that with:

javac Assert.java
javap -c -constants -private -verbose Assert.class

With Oracle JDK 1.8.0_45, a synthetic static field was generated (see also: What is the meaning of "static synthetic"?):

static final boolean $assertionsDisabled;
  descriptor: Z
  flags: ACC_STATIC, ACC_FINAL, ACC_SYNTHETIC

together with a static initializer:

 0: ldc           #6                  // class Assert
 2: invokevirtual #7                  // Method java/lang Class.desiredAssertionStatus:()Z
 5: ifne          12
 8: iconst_1
 9: goto          13
12: iconst_0
13: putstatic     #2                  // Field $assertionsDisabled:Z
16: return

and the main method is:

 0: getstatic     #2                  // Field $assertionsDisabled:Z
 3: ifne          22
 6: invokestatic  #3                  // Method java/lang/System.currentTimeMillis:()J
 9: lconst_0
10: lcmp
11: ifeq          22
14: new           #4                  // class java/lang/AssertionError
17: dup
18: invokespecial #5                  // Method java/lang/AssertionError."<init>":()V
21: athrow
22: return

We conclude that:

  • there is no bytecode level support for assert: it is a Java language concept
  • assert could be emulated pretty well with system properties -Pcom.me.assert=true to replace -ea on the command line, and a throw new AssertionError().



回答8:


An assertion allows for detecting defects in the code. You can turn on assertions for testing and debugging while leaving them off when your program is in production.

Why assert something when you know it is true? It is only true when everything is working properly. If the program has a defect, it might not actually be true. Detecting this earlier in the process lets you know something is wrong.

An assert statement contains this statement along with an optional String message.

The syntax for an assert statement has two forms:

assert boolean_expression;
assert boolean_expression: error_message;

Here are some basic rules which govern where assertions should be used and where they should not be used. Assertions should be used for:

  1. Validating input parameters of a private method. NOT for public methods. public methods should throw regular exceptions when passed bad parameters.

  2. Anywhere in the program to ensure the validity of a fact which is almost certainly true.

For example, if you are sure that it will only be either 1 or 2, you can use an assertion like this:

...
if (i == 1)    {
    ...
}
else if (i == 2)    {
    ...
} else {
    assert false : "cannot happen. i is " + i;
}
...
  1. Validating post conditions at the end of any method. This means, after executing the business logic, you can use assertions to ensure that the internal state of your variables or results is consistent with what you expect. For example, a method that opens a socket or a file can use an assertion at the end to ensure that the socket or the file is indeed opened.

Assertions should not be used for:

  1. Validating input parameters of a public method. Since assertions may not always be executed, the regular exception mechanism should be used.

  2. Validating constraints on something that is input by the user. Same as above.

  3. Should not be used for side effects.

For example this is not a proper use because here the assertion is used for its side effect of calling of the doSomething() method.

public boolean doSomething() {
...    
}
public void someMethod() {       
assert doSomething(); 
}

The only case where this could be justified is when you are trying to find out whether or not assertions are enabled in your code:   

boolean enabled = false;    
assert enabled = true;    
if (enabled) {
    System.out.println("Assertions are enabled");
} else {
    System.out.println("Assertions are disabled");
}



回答9:


A real world example, from a Stack-class (from Assertion in Java Articles)

public int pop() {
   // precondition
   assert !isEmpty() : "Stack is empty";
   return stack[--num];
}



回答10:


In addition to all the great answers provided here, the official Java SE 7 programming guide has a pretty concise manual on using assert; with several spot-on examples of when it's a good (and, importantly, bad) idea to use assertions, and how it's different from throwing exceptions.

Link




回答11:


Assert is very useful when developing. You use it when something just cannot happen if your code is working correctly. It's easy to use, and can stay in the code for ever, because it will be turned off in real life.

If there is any chance that the condition can occur in real life, then you must handle it.

I love it, but don't know how to turn it on in Eclipse/Android/ADT . It seems to be off even when debugging. (There is a thread on this, but it refers to the 'Java vm', which does not appear in the ADT Run Configuration).




回答12:


Here's an assertion I wrote in a server for a Hibernate/SQL project. An entity bean had two effectively-boolean properties, called isActive and isDefault. Each could have a value of "Y" or "N" or null, which was treated as "N". We want to make sure the browser client is limited to these three values. So, in my setters for these two properties, I added this assertion:

assert new HashSet<String>(Arrays.asList("Y", "N", null)).contains(value) : value;

Notice the following.

  1. This assertion is for the development phase only. If the client sends a bad value, we will catch that early and fix it, long before we reach production. Assertions are for defects that you can catch early.

  2. This assertion is slow and inefficient. That's okay. Assertions are free to be slow. We don't care because they're development-only tools. This won't slow down the production code because assertions will be disabled. (There's some disagreement on this point, which I'll get to later.) This leads to my next point.

  3. This assertion has no side effects. I could have tested my value against an unmodifiable static final Set, but that set would have stayed around in production, where it would never get used.

  4. This assertion exists to verify the proper operation of the client. So by the time we reach production, we will be sure that the client is operating properly, so we can safely turn the assertion off.

  5. Some people ask this: If the assertion isn't needed in production, why not just take them out when you're done? Because you'll still need them when you start working on the next version.

Some people have argued that you should never use assertions, because you can never be sure that all the bugs are gone, so you need to keep them around even in production. And so there's no point in using the assert statement, since the only advantage to asserts is that you can turn them off. Hence, according to this thinking, you should (almost) never use asserts. I disagree. It's certainly true that if a test belongs in production, you should not use an assert. But this test does not belong in production. This one is for catching a bug that's not likely to ever reach production, so it may safely be turned off when you're done.

BTW, I could have written it like this:

assert value == null || value.equals("Y") || value.equals("N") : value;

This is fine for only three values, but if the number of possible values gets bigger, the HashSet version becomes more convenient. I chose the HashSet version to make my point about efficiency.




回答13:


Assertion are basically used to debug the application or it is used in replacement of exception handling for some application to check the validity of an application.

Assertion works at run time. A simple example, that can explain the whole concept very simply, is herein - What does the assert keyword do in Java? (WikiAnswers).




回答14:


Assertions are disabled by default. To enable them we must run the program with -ea options (granularity can be varied). For example, java -ea AssertionsDemo.

There are two formats for using assertions:

  1. Simple: eg. assert 1==2; // This will raise an AssertionError.
  2. Better: assert 1==2: "no way.. 1 is not equal to 2"; This will raise an AssertionError with the message given displayed too and is thus better. Although the actual syntax is assert expr1:expr2 where expr2 can be any expression returning a value, I have used it more often just to print a message.



回答15:


To recap (and this is true of many languages not just Java):

"assert" is primarily used as a debugging aid by software developers during the debugging process. Assert-messages should never appear. Many languages provide a compile-time option that will cause all "asserts" to be ignored, for use in generating "production" code.

"exceptions" are a handy way to handle all kinds of error conditions, whether or not they represent logic errors, because, if you run into an error-condition such that you cannot continue, you can simply "throw them up into the air," from wherever you are, expecting someone else out there to be ready to "catch" them. Control is transferred in one step, straight from the code that threw the exception, straight to the catcher's mitt. (And the catcher can see the complete backtrace of calls that had taken place.)

Furthermore, callers of that subroutine don't have to check to see if the subroutine succeeded: "if we're here now, it must have succeeded, because otherwise it would have thrown an exception and we wouldn't be here now!" This simple strategy makes code-design and debugging much, much easier.

Exceptions conveniently allow fatal-error conditions to be what they are: "exceptions to the rule." And, for them to be handled by a code-path that is also "an exception to the rule ... "fly ball!"




回答16:


Basically, "assert true" will pass and "assert false" will fail. Let's looks at how this will work:

public static void main(String[] args)
{
    String s1 = "Hello";
    assert checkInteger(s1);
}

private static boolean checkInteger(String s)
{
    try {
        Integer.parseInt(s);
        return true;
    }
    catch(Exception e)
    {
        return false;
    }
}



回答17:


Assertions are checks which may get switched off. They're rarely used. Why?

  • They must not be used for checking public method arguments as you have no control over them.
  • They should not be used for simple checks like result != null as such checks are very fast and there's hardly anything to save.

So, what's left? Expensive checks for conditions really expected to be true. A good example would be the invariants of a data structure like RB-tree. Actually, in ConcurrentHashMap of JDK8, there are a few such meaningful asserts for the TreeNodes.

  • You really don't want to switch them on in production as they could easily dominate the run time.
  • You may want to switch them on or off during tests.
  • You definitely want to switch them on when working on the code.

Sometimes, the check is not really expensive, but at the same time, you're pretty sure, it'll pass. In my code, there's e.g.,

assert Sets.newHashSet(userIds).size() == userIds.size();

where I'm pretty sure that the list I just created has unique elements, but I wanted to document and double check it.




回答18:


assert is a keyword. It was introduced in JDK 1.4. The are two types of asserts

  1. Very simple assert statements
  2. Simple assert statements.

By default all assert statements will not be executed. If an assert statement receives false, then it will automatically raise an assertion error.



来源:https://stackoverflow.com/questions/2758224/what-does-the-java-assert-keyword-do-and-when-should-it-be-used

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!