Утверждения - это инструмент фазы разработки для выявления ошибок в вашем коде. Они предназначены для легкого удаления, поэтому они не будут существовать в рабочем коде. Таким образом, утверждения не являются частью «решения», которое вы предоставляете клиенту. Это внутренние проверки, чтобы убедиться, что ваши предположения верны. Наиболее распространенным примером является проверка на ноль. Многие методы написаны так:
void doSomething(Widget widget) {
if (widget != null) {
widget.someMethod(); // ...
... // do more stuff with this widget
}
}
Очень часто в таком методе виджет никогда не должен быть нулевым. Так что, если он нулевой, в вашем коде есть ошибка, которую нужно отследить. Но код выше никогда не скажет вам этого. Поэтому, стараясь написать «безопасный» код, вы также скрываете ошибку. Гораздо лучше написать такой код:
/**
* @param Widget widget Should never be null
*/
void doSomething(Widget widget) {
assert widget != null;
widget.someMethod(); // ...
... // do more stuff with this widget
}
Таким образом, вы наверняка поймаете эту ошибку рано. (Также полезно указать в контракте, что этот параметр никогда не должен быть нулевым.) Обязательно включайте утверждения при тестировании кода во время разработки. (И убедить ваших коллег сделать это тоже часто бывает сложно, что я нахожу очень раздражающим.)
Теперь некоторые из ваших коллег будут возражать против этого кода, утверждая, что вы все равно должны поставить нулевую проверку, чтобы предотвратить исключение в рабочей среде. В этом случае утверждение все еще полезно. Вы можете написать это так:
void doSomething(Widget widget) {
assert widget != null;
if (widget != null) {
widget.someMethod(); // ...
... // do more stuff with this widget
}
}
Таким образом, ваши коллеги будут рады, что для производственного кода есть нулевая проверка, но во время разработки вы больше не будете скрывать ошибку, когда виджет равен нулю.
Вот пример из реальной жизни: однажды я написал метод, который сравнивал два произвольных значения на равенство, где любое значение могло быть нулевым:
/**
* 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;
}
Этот код делегирует работу метода equals()
в случае, когда thisValue не равно NULL. Но предполагается, что метод equals()
правильно выполняет контракт equals()
, правильно обрабатывая нулевой параметр.
Коллега возразил против моего кода, сказав, что во многих наших классах есть ошибочные equals()
методы, которые не проверяют на ноль, поэтому я должен поставить эту проверку в этот метод. Это спорный вопрос, если это имеет смысл, или если мы должны заставить эту ошибку, так что мы можем обнаружить его и исправить ее, но я отложила мой коллега и поставить в нулевом чеке, который я помеченный комментарий:
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;
}
Дополнительная проверка здесь, other != null
, необходима только в том случае, если метод equals()
не может проверить на ноль, как того требует его контракт.
Вместо того, чтобы вступать в бесполезную дискуссию с моим коллегой о целесообразности сохранения ошибочного кода в нашей кодовой базе, я просто вставил в него два утверждения. Эти утверждения дадут мне знать, на одном этапе разработки, если один из наших классов не сможет правильно реализовать equals()
, поэтому я могу это исправить:
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;
}
Важно помнить следующее:
Утверждения являются только инструментами этапа разработки.
Смысл утверждения в том, чтобы сообщить вам, если есть ошибка, не только в вашем коде, но и в вашей кодовой базе . (Утверждения здесь на самом деле помечают ошибки в других классах.)
Даже если бы мой коллега был уверен, что наши занятия написаны правильно, утверждения здесь все равно будут полезны. Будут добавлены новые классы, которые могут не пройти проверку на нулевое значение, и этот метод может пометить нам эти ошибки.
В процессе разработки вы всегда должны включать утверждения, даже если написанный вами код не использует утверждения. Моя IDE по умолчанию всегда делает это для любого нового исполняемого файла.
Утверждения не меняют поведение кода в производственной среде, поэтому мой коллега рад, что проверка на ноль есть, и что этот метод будет выполняться правильно, даже если метод equals()
содержит ошибки. Я счастлив, потому что я поймаю любой ошибочный метод equals()
в разработке.
Кроме того, вы должны проверить свою политику утверждений, вставив временное утверждение, которое не будет выполнено, чтобы вы могли быть уверены, что вас уведомят либо через файл журнала, либо через трассировку стека в выходном потоке.