Передача null методу

Я сейчас читаю отличный Чистый код

Одно обсуждение касается передачи значений NULL в метод.

public class MetricsCalculator {
    public double xProjection(Point p1, Point p2) {
        return (p2.x - p1.x) * 1.5;
    }
}
...
calculator.xProjection(null, new Point(12,13));

Он представляет собой разные способы решения этой проблемы:

public double xProjection(Point p1, Point p2) {
    if (p1 == null || p2 == null) {
        throw new IllegalArgumentException("Invalid argument for xProjection");
    }
    return (p2.x - p1.x) * 1.5;
}

public double xProjection(Point p1, Point p2) {
    assert p1 != null : "p1 should not be null";
    assert p2 != null : "p2 should not be null";
    return (p2.x - p1.x) * 1.5;
}

Я предпочитаю подход утверждения, но мне не нравится, что утверждения по умолчанию отключены.

В книге, наконец, говорится:

In most programming languages there is no good way to deal with a null that is passed by a caller accidentally. Because this is the case, the rational approach is to forbid passing null by default.

На самом деле это не касается того, как вы будете применять это ограничение?

Есть ли у кого-нибудь из вас твердое мнение.

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
18
0
39 929
16
Перейти к ответу Данный вопрос помечен как решенный

Ответы 16

Я обычно предпочитаю не делать то же самое, потому что это просто замедляет работу. В любом случае исключения NullPointerExceptions выбрасываются позже, что быстро приведет к тому, что пользователь обнаружит, что они передают null методу. Раньше я проверял, но 40% моего кода заканчивались проверкой кода, после чего я решил, что это просто не стоит красивых сообщений с утверждениями.

It doesn't really go into how you would enforce this restriction?

Вы обеспечиваете его соблюдение, бросая ArgumentExcexception, если они проходят в null.

if (p1 == null || p2 == null) {
    throw new IllegalArgumentException("Invalid argument for xProjection");
}

Я согласен или не согласен с сообщение wvdschel, это зависит от того, что он конкретно говорит.

В этом случае, конечно, этот метод выйдет из строя на null, поэтому явная проверка здесь, вероятно, не нужна.

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

Я предпочитаю использовать утверждения.

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

From docs.oracle.com/javase/7/docs/technotes/guides/language/… Не использовать утверждения для проверки аргументов в общедоступных методах. Проверка аргументов обычно является частью опубликованных спецификаций (или контракта) метода, и эти спецификации должны соблюдаться независимо от того, разрешены или запрещены утверждения. Другая проблема с использованием утверждений для проверки аргументов заключается в том, что ошибочные аргументы должны приводить к соответствующему исключению времени выполнения (IllegalArgumentException, IndexOutOfBoundsException и т. д.). Ошибка утверждения не вызовет соответствующего исключения.

Md. Abu Nafee Ibna Zahid 27.12.2017 14:01

Общее правило: если ваш метод не ожидает аргументов null, вы должны бросить System.ArgumentNullException. Использование надлежащего Exception не только защищает вас от повреждения ресурсов и других плохих вещей, но и служит руководством для пользователей вашего кода, экономя время, затрачиваемое на отладку вашего кода.

Также читайте статью о Защитное программирование

Это хороший ответ C#, не такой хороший ответ на Java :)

John Gardner 30.10.2008 04:27

@Chris Karcher Я бы сказал абсолютно правильно. Единственное, что я бы сказал, это проверить параметры отдельно и иметь в отчете об исключениях параметр, который был равен нулю, так как это значительно упрощает отслеживание того, откуда исходит значение null.

@wvdschel вау! Если написание кода является для вас слишком трудоемким, вам следует изучить что-то вроде PostSharp (или эквивалент Java, если он доступен), который может постобработать ваши сборки и вставить для вас проверки параметров.

Хотя это не является строго связанным, вы можете взглянуть на Спецификация #.

Я думаю, что он все еще находится в разработке (Microsoft), но доступны некоторые CTP, и это выглядит многообещающим. В основном это позволяет вам делать это:

  public static int Divide(int x, int y)
    requires y != 0 otherwise ArgumentException; 
  {
  }

или же

  public static int Subtract(int x, int y)
    requires x > y;
    ensures result > y;
  {
    return x - y;
  } 

Он также предоставляет другие функции, такие как типы Notnull. Он построен на основе .NET Framework 2.0 и полностью совместим. Как видите, синтаксис - это C#.

SpeC# выглядит очень интересно!

Когда что-то подобное недоступно, я обычно тестирую непубличные методы с помощью проверки на null во время выполнения и утверждений для внутренних методов. Вместо того, чтобы явно кодировать нулевую проверку в каждом методе, я делегирую ее классу утилит с помощью нулевого метода проверки:

/**
 * Checks to see if an object is null, and if so 
 * generates an IllegalArgumentException with a fitting message.
 * 
 * @param o The object to check against null.
 * @param name The name of the object, used to format the exception message
 *
 * @throws IllegalArgumentException if o is null.
 */
public static void checkNull(Object o, String name) 
    throws IllegalArgumentException {
   if (null == o)
      throw new IllegalArgumentException(name + " must not be null");
}

public static void checkNull(Object o) throws IllegalArgumentException {
   checkNull(o, "object");
} 

// untested:
public static void checkNull(Object... os) throws IllegalArgumentException {
   for(Object o in os) checkNull(o);  
}

Тогда проверка превращается в:

public void someFun(String val1, String val2) throws IllegalArgumentException {
   ExceptionUtilities.checkNull(val1, "val1");
   ExceptionUtilities.checkNull(val2, "val2");

   /** alternatively:
   ExceptionUtilities.checkNull(val1, val2);
   **/

   /** ... **/
} 

Который можно добавить с помощью макросов редактора или сценария обработки кода. Редактировать: Таким же образом можно добавить подробную проверку, но я думаю, что значительно проще автоматизировать добавление одной строки.

Также не для немедленного использования, но связано с упоминанием SpeC# ... Есть предложение добавить "нулевые безопасные типы" в будущую версию Java: «Улучшенная обработка NULL - Нулевые типы».

Согласно предложению, ваш метод станет

public class MetricsCalculator {
    public double xProjection(#Point p1, #Point p2) {
        return (p2.x - p1.x) * 1.5;
    }
}

где #Point - это тип не-null ссылок на объекты типа Point.

Я действительно не знаю, хочу ли я, чтобы это было частью Java, но это очень интересно.

Guido 30.10.2008 01:51

Использование C# ArgumentException или Java IllegalArgumentException в самом начале метода кажется мне наиболее ясным решением.

Всегда следует быть осторожным с исключениями времени выполнения - исключениями, которые не объявлены в сигнатуре метода. Поскольку компилятор не заставляет вас их ловить, о них очень легко забыть. Убедитесь, что у вас есть какая-то система обработки исключений "перехватить все", чтобы предотвратить внезапную остановку программы. Это самая важная часть вашего пользовательского опыта.

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

In most programming languages there is no good way to deal with a null that is passed by a caller accidentally. Because this is the case, the rational approach is to forbid passing null by default.

Я нашел подход к аннотациям JetBrains '@Nullable и @NotNull для работы с этим самым гениальным до сих пор. К сожалению, это IDE, но действительно чистый и мощный, IMO.

http://www.jetbrains.com/idea/documentation/howto.html

Было бы неплохо иметь это (или что-то подобное) в качестве стандарта Java.

Ответ принят как подходящий

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

  • Утверждения имеют преимущество в производительности, поскольку они обычно отключены в производственных системах.
  • Исключения имеют преимущество безопасности, поскольку проверка выполняется всегда.

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

На практике, однако, я знаю много разработчиков, которые не верят, что утверждения будут включены, когда это необходимо, и поэтому выбирают безопасное создание исключения NullPointerException.

Конечно, если вы не можете обеспечить соблюдение политики для своего кода (например, если вы создаете библиотеку и поэтому зависят от того, как другие разработчики запускают ваш код), вам следует выбрать безопасный подход, выбрасывая NullPointerException для тех, кто методы, которые являются частью API библиотеки.

Немного не по теме, но одна функция findbugs, которая, на мой взгляд, очень полезна, - это возможность аннотировать параметры методов, чтобы описать, каким параметрам не следует передавать нулевое значение.

Используя статический анализ вашего кода, findbugs может затем указать места, где вызывается метод с потенциально нулевым значением.

У этого есть два преимущества:

  1. Аннотация описывает ваше намерение относительно того, как следует вызывать метод, помогая в документации.
  2. FindBugs может указывать на потенциальных проблемных вызывающих метод, позволяя отслеживать потенциальные ошибки.

Полезно только тогда, когда у вас есть доступ к коду, вызывающему ваши методы, но это обычно так.

На языке Java, если предположить, что значение null возникает из-за ошибки программирования (т. Е. Никогда не должно выходить за пределы фазы тестирования), оставьте систему, чтобы выбросить его, или, если есть побочные эффекты, достигающие этой точки, проверьте наличие null в начале и бросить либо IllegalArgumentException, либо NullPointerException.

Если нуль может быть получен из фактического случая исключениеal, но вы не хотите использовать для этого проверенное исключение, тогда вы определенно захотите перейти по маршруту IllegalArgumentException в начале метода.

Поскольку похоже, что тема не по теме стала темой, Scala применяет к этому интересный подход. Предполагается, что все типы не равны нулю, если только вы явно не заключите его в Option, чтобы указать, что он может быть нулевым. Так:

//  allocate null
var name : Option[String]
name = None

//  allocate a value
name = Any["Hello"]

//  print the value if we can
name match {
  Any[x] => print x
  _ => print "Nothing at all"
}

Интересно :-) нужно будет прочитать о scala.

toolkit 30.10.2008 14:27

Другие вопросы по теме