Проверить параметры, аннотированные @Nonnull, на ноль?

19

Мы начали использовать FindBugs и соответствующим образом аннотировали наши параметры @Nonnull, и это замечательно работает для выявления ошибок в начале цикла. До сих пор мы продолжали проверять эти аргументы для nullиспользования гуавы checkNotNull, но я бы предпочел проверять nullтолько на краях - в местах, где значение может входить без проверки null, например, на запрос SOAP.

// service layer accessible from outside
public Person createPerson(@CheckForNull String name) {
    return new Person(Preconditions.checkNotNull(name));
}

...

// internal constructor accessed only by the service layer
public Person(@Nonnull String name) {
    this.name = Preconditions.checkNotNull(name);  // remove this check?
}

Я понимаю, что @Nonnullне блокирует nullценности сам.

Однако, учитывая, что FindBugs будет указывать везде, где значение передается из немаркированного поля в одно помеченное @Nonnull, мы не можем рассчитывать на то, что он отлавливает эти случаи (что и происходит), не проверяя эти значения nullвезде, где они передаются в система? Я наивный, чтобы хотеть доверять инструменту и избегать этих подробных проверок?

Итог: хотя, кажется, безопасно убрать вторую nullпроверку ниже, это плохая практика?

Этот вопрос, возможно, слишком похож на вопрос « Нужно ли проверять на ноль», если он не ожидает «ноль» , но я спрашиваю конкретно относительно @Nonnullаннотации.

Дэвид Харкнесс
источник

Ответы:

6

Если вы не доверяете FindBug, возможно, это вариант использования утверждения. Таким образом, вы избежите проблем, упомянутых пользователем MSalters, но все равно будете иметь проверку. Конечно, этот подход может быть неприменим, если такой отказоустойчивый подход невозможен, т. Е. Вы должны поймать любое исключение.

И для дальнейшего ответа на вопрос, является ли это плохой практикой. Ну, это спорно, но я бы не думаю. Я рассматриваю эту аннотацию FindBug как легкую спецификацию, следующую дизайну по принципу контракта.

При проектировании по контракту вы заключаете контракт между методом и его вызывающей стороной. Контракт состоит из предварительных условий, которые вызывающая сторона соглашается выполнить при вызове метода, и постусловия, которое, в свою очередь, выполняется методом после выполнения, если выполняется его предварительное условие.

Одним из преимуществ этого метода разработки является то, что вы можете избежать так называемого стиля защитного программирования (в котором вы явно проверяете все недопустимые значения параметров и т. Д.). Вместо этого вы можете полагаться на контракт и избегать избыточных проверок для повышения производительности и читабельности.

Контракты могут быть использованы для проверки утверждений во время выполнения, которая следует вышеупомянутому принципу «первый сбой», в котором вы хотите, чтобы ваша программа потерпела неудачу в случае разрыва контракта, что дает вам ключ к идентификации источника ошибки. (Неудачное предварительное условие означает, что вызывающая сторона сделала что-то не так, неудачное постусловие указывает на ошибку реализации данного метода)

Кроме того, контракты могут использоваться для статического анализа, который пытается сделать выводы об исходном коде, фактически не выполняя его.

Аннотация @nonnull может рассматриваться как предварительное условие, которое используется для статического анализа инструментом FindBugs. Если вы предпочитаете такой подход, как проверка утверждений во время выполнения, т. Е. Стратегия сбой в первую очередь, вам следует рассмотреть возможность использования операторов утверждений в качестве встроенного в Java. Или, может быть, адаптироваться к более сложной стратегии спецификации, используя язык спецификации поведенческого интерфейса, такой как язык моделирования Java (JML).

JML является расширением Java, спецификация которого встроена в специальные комментарии Java. Преимущество этого подхода состоит в том, что вы можете использовать сложную спецификацию, даже используя подход разработки, в котором вы полагаетесь только на спецификацию, а не на детали реализации, и используете различные инструменты, которые используют спецификацию, такие как упомянутые инструменты проверки утверждений во время выполнения, автоматизированная генерация юнит-тестов или документации.

Если вы разделяете мою точку зрения о том, что @nonnull является (легким) контрактом, было бы обычной практикой не добавлять дополнительные проверки, потому что первоначальная идея этого принципа - избежать защитного программирования.

FabianB
источник
2
Это именно то, как я использую @Nonnull: в качестве спецификации контракта. Я убрал защитные проверки за контрактом и до сих пор не было никаких проблем.
Дэвид Харкнесс
4

Ну, подумай, почему ты не пишешь

this.name = Preconditions.checkNotNull(name);  // Might be necessary
that.name = Preconditions.checkNotNull(this.name);  // Who knows?

Программирование не черная магия. Переменные не становятся внезапно пустыми. Если вы знаете, что переменная не является нулевой, и вы знаете, что она не изменилась, не проверяйте ее.

Если вы все-таки проверите это, какой-нибудь программист в будущем (возможно, вы) потратите много времени, чтобы выяснить, почему эта проверка есть. В конце концов, чек должен быть там по какой-то причине, так что же вы упускаете?

MSalters
источник
3
В @Nonnullконтракте оговаривает , что абоненты не должны передать nullв конструктор, и FindBugs использует статический анализ для обнаружения вызовов , которые могли бы позволить null--specifically вызовов , которые проходят значения не помеченные @Nonnullсебя. Но вызывающий может свободно передавать nullи игнорировать предупреждение от FindBugs.
Дэвид Харкнесс
Программирование в идеале не черная магия. К сожалению, предостережений и неожиданностей предостаточно, особенно при работе с некоторыми кодами параллелизма ♬ ~ ᕕ (ᐛ) ᕗ
Тим Харпер,