Assert use cases
常用的用assert场景:
先决条件(private方法中) - 方法调用者必须满足这些需求
后置条件 - 检查结果
类不变式class invariant - 检查对象状态
运行时不可达代码 - 某些分支预期不可达,但无法在编译器做检查(常见在else分支或switch的default分支)
下面是例子
import java.util.Random;
public final class Flower {
public static void main(String... arguments) {
final Flower tulip = new Flower("Tulip", 1);
tulip.grow();
tulip.grow();
System.out.println(tulip);
tulip.randomGrowOrWither();
System.out.println(tulip);
tulip.wither();
tulip.wither();
System.out.println(tulip);
}
/**
* @param aSpecies must have content.
* @param aInitialLength must be greater than 0.
*/
public Flower(final String aSpecies, final int aInitialLength) {
// 不要用assert来检查public方法的参数
if (!isValidSpecies(aSpecies)) {
throw new IllegalArgumentException("Species must have content.");
}
if (!isValidLength(aInitialLength)) {
throw new IllegalArgumentException("Initial length must be positive.");
}
fSpecies = aSpecies;
fLength = aInitialLength;
// 检查类不变式
assert hasValidState(): "Construction failed - not valid state.";
}
public boolean isMature() {
return fLength > 5 ;
// 无需assert, 因为对象状态没有改变
}
/**
* Increase the length by at least one unit.
*/
public void grow(){
// 由于fLength总是被copy了一次,不管是否打开了assert
// 这种风格的后置条件检查是不推荐的。后面的wither有一个改良版本的。
final int oldLength = fLength;
fLength += getLengthIncrease(fLength);
//post-condition: length has increased
assert fLength > oldLength;
// 检查类不变式
assert hasValidState(): this;
}
/**
* Decrease the length by one unit, but only if the resulting length
* will still be greater than 0.
*/
public void wither(){
// 这个局部类的存在是为了获取当时状态的一个快照,尽管有些笨重,但这种风格能作任意复杂的
// 后置条件检查
class OriginalState {
OriginalState() {
fOriginalLength = fLength;
}
int getLength() {
return fOriginalLength;
}
private final int fOriginalLength;
}
OriginalState originalState = null;
// 在assert函数里面构造对象,可以用于确保assert被关闭后不会有构造发生。
// 这个assert有一点不一样的地方是他总是成功,并且有负作用,它构造了一个对象并保留了他的引用.
assert (originalState = new OriginalState()) != null;
if (fLength > 1) {
--fLength;
}
// 后置条件检查: 长度减少1或保持不变
assert fLength <= originalState.getLength();
// 检查类不变式
assert hasValidState(): this;
}
/**
* Randomly select one of three actions
* <ul>
* <li>do nothing
* <li>grow
* <li>wither
* </ul>
*/
public void randomGrowOrWither() {
//(magic numbers are used here instead of symbolic constants
//to slightly clarify the example)
Random generator = new Random();
int action = generator.nextInt(3);
//according to the documentation for the Random class, action
//should take one of the values 0,1,2.
if (action == 0) {
//do nothing
}
else if (action == 1) {
grow();
}
else if (action == 2) {
wither();
}
else {
// assert被禁止,仍有可能运行到这里
throw new AssertionError("Unexpected value for action: " + action);
}
// 检查类不变式
assert hasValidState(): this;
}
/** Use for debugging only. */
public String toString(){
final StringBuilder result = new StringBuilder();
result.append(this.getClass().getName());
result.append(": Species=");
result.append(fSpecies);
result.append(" Length=");
result.append(fLength);
return result.toString();
}
// PRIVATE
private final String fSpecies;
private int fLength;
/**
* Implements the class invariant.
*
* Perform all checks on the state of the object.
* One may assert that this method returns true at the end
* of every public method.
*/
private boolean hasValidState(){
return isValidSpecies(fSpecies) && isValidLength(fLength);
}
/** Species must have content. */
private boolean isValidSpecies(final String aSpecies) {
return aSpecies != null && aSpecies.trim().length()>0;
}
/** Length must be greater than 0. */
private boolean isValidLength(final int aLength) {
return aLength > 0;
}
/** Length increase depends on current length. */
private int getLengthIncrease(int aOriginalLength) {
// 由于这是一个私有方法,这里用assert来检验参数的合法性
assert aOriginalLength > 0: this;
int result = 0;
if (aOriginalLength > 10) {
result = 2;
}
else {
result = 1;
}
assert result > 0 : result;
return result;
}
}