Вопрос

Я нахожусь в середине чтения превосходного Чистый код

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

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;
}

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

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

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

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

В любом случае, есть ли у кого-нибудь из вас твердое мнение?

Это было полезно?

Решение

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

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

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

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

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

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

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

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

Также не имеет непосредственного применения, но связан с упоминанием спецификации #...Есть предложение добавить "null-безопасные типы" в будущую версию Java: "Улучшенная обработка нулей - Null-безопасные типы".

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

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

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

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

Вы приводите это в исполнение, бросая Исключение ArgumentExcexception если они передают значение null.

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

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

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

Спецификация # выглядит очень интересно!

Когда что-то подобное недоступно, я обычно тестирую непубличные методы с помощью нулевой проверки во время выполнения и утверждений для внутренних методов.Вместо того чтобы явно кодировать проверку null в каждом методе, я делегирую ее классу utilities с методом check 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);
   **/

   /** ... **/
} 

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

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

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

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

Иметь это (или что-то подобное) в качестве стандарта 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 #.

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

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

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

//  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"
}

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

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

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

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

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

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

Это имеет два преимущества:

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

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

Размораживание C# ArgumentException, или Java IllegalArgumentException в самом начале этот метод кажется мне самым ясным из решений.

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top