Spec-Zone .ru
спецификации, руководства, описания, API
Содержание документации

Программирование С Утверждениями

Утверждение является оператором в языке программирования JavaTM, который позволяет Вам протестировать свои предположения о Вашей программе. Например, если Вы пишете метод, который вычисляет скорость частицы, Вы могли бы утверждать, что расчетная скорость является меньше чем скорость света.

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

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

Этот документ показывает Вам, как программировать с утверждениями. Это затрагивает темы:


Введение

У оператора контроля есть две формы. Первая, более простая форма:

assert Expression1 ;

где Expression1 является a boolean выражение. Когда система выполняет утверждение, она оценивает Expression1 и если это false броски AssertionError без сообщения детали.

Вторая форма оператора контроля:

assert Expression1 : Expression2 ;

где:

Используйте эту версию assert оператор, чтобы обеспечить деталь обменивается сообщениями для AssertionError. Система передает значение Expression2 к соответствующему AssertionError конструктор, который использует строковое представление значения как сообщение детали ошибки.

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

Как все непойманные исключения, отказы утверждения обычно маркируются в трассировке стека с файлом и номером строки, от которого они были брошены. Вторая форма оператора контроля должна использоваться в предпочтении к первому только, когда у программы есть некоторая дополнительная информация, которая могла бы помочь диагностировать отказ. Например, если Expression1 включает отношение между двумя переменными x и y, вторая форма должна использоваться. При этих обстоятельствах разумный кандидат на Expression2 был бы "x: " + x + ", y: " + y.

В некоторых случаях Expression1 может быть дорогим, чтобы оценить. Например, предположите, что Вы пишете метод, чтобы найти минимальный элемент в несортированном списке, и Вы добавляете утверждение, чтобы проверить, что выбранный элемент является действительно минимумом. Работа, сделанная утверждением, будет, по крайней мере, столь же дорога как работа, сделанная методом непосредственно. Чтобы гарантировать, что утверждения не являются ответственностью производительности в развернутых приложениях, утверждения могут быть включены или отключены, когда программа запускается, и отключается по умолчанию. Отключение утверждений устраняет их потерю производительности полностью. После того, как отключенный, они чрезвычайно эквивалентны пустым операторам в семантике и производительности. См. Включение и Отключение Утверждений для получения дополнительной информации.

Добавление assert у ключевого слова к языку программирования Java есть импликации для существующего кода. См. Совместимость С Существующими Программами для получения дополнительной информации.

Помещение Утверждений В Ваш Код

Есть много ситуаций, где хорошо использовать утверждения, включая:

Есть также ситуации, где недопустимо использовать их:

Внутренние Инварианты

Прежде, чем утверждения были доступны, много программистов используемые комментарии, чтобы указать на их предположения относительно поведения программы. Например, Вы, возможно, записали что-то вроде этого, чтобы объяснить Ваше предположение о else пункт в многоканальном операторе "if":

if (i % 3 == 0) {
    ...
} else if (i % 3 == 1) {
    ...
} else { // We know (i % 3 == 2)
    ...
}

Следует теперь использовать утверждение всякий раз, когда Вы записали бы комментарий, который утверждает инвариант. Например, следует переписать предыдущий оператор "if" как это:

if (i % 3 == 0) {
   ...
} else if (i % 3 == 1) {
    ...
} else {
    assert i % 3 == 2 : i;
    ...
}

Отметьте, случайно, что утверждение в вышеупомянутом примере может перестать работать если i отрицательно, как % оператор не является истинным оператором модуля, но вычисляет остаток, который может быть отрицательным.

Другой хороший кандидат на утверждение является a switch оператор без default случай. Отсутствие a default случай обычно указывает, что программист полагает, что один из случаев будет всегда выполняться. Предположение, что у определенной переменной будет одно из небольшого количества значений, является инвариантом, который должен быть проверен с утверждением. Например, предположите следующий switch оператор появляется в программе, которая обрабатывает игру в карты:

switch(suit) {
  case Suit.CLUBS:
    ...
  break;

  case Suit.DIAMONDS:
    ...
  break;

  case Suit.HEARTS:
    ...
    break;

  case Suit.SPADES:
      ...
}

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

default:
    assert false : suit;

Если suit переменная берет другое значение, и утверждения включаются, утверждение перестанет работать и AssertionError будет брошен.

Приемлемая альтернатива:

default:
    throw new AssertionError(suit);

Эта альтернатива предлагает защиту, даже если утверждения отключаются, но дополнительная защита не добавляет стоимости: throw оператор не будет выполняться, если программа не перестала работать. Кроме того альтернатива является законной при некоторых обстоятельствах где assert оператор не. Если метод включения возвращает значение, каждый случай в switch оператор содержит a return оператор, и нет return оператор следует switch оператор, тогда это вызвало бы синтаксическую ошибку добавить случай по умолчанию с утверждением. (Метод возвратился бы без значения, если бы никакой соответствующий случай и утверждения не был отключен.)

Инварианты потока управления

Предыдущий пример не только тестирует инвариант, он также проверяет предположение о потоке приложения управления. Автор оригинала switch оператор, вероятно, принятый не только, что suit у переменной всегда было бы одно из четырех значений, но также и что один из этих четырех случаев будет всегда выполняться. Это указывает на другую общую область, где следует использовать утверждения: поместите утверждение в любое расположение, которое Вы принимаете, не будет достигнут. Оператор утверждений, чтобы использовать:

assert false;

Например, предположите, что у Вас есть метод, который похож на это:

void foo() {
    for (...) {
      if (...)
        return;
    }
    // Execution should never reach this point!!!
}

Замените заключительный комментарий так, чтобы код теперь читал:

void foo() {
    for (...) {
      if (...)
        return;
    }
    assert false; // Execution should never reach this point!
}

Отметьте: Используйте этот метод с усмотрением. Если оператор будет недостижим как определено в Спецификации языка Java, то Вы получите ошибку времени компиляции, если Вы попытаетесь утверждать, что это не достигается. Снова, приемлемая альтернатива должна просто бросить AssertionError.

Предварительные условия, Постусловия, и Инварианты Класса

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

Предварительные условия

Условно, предварительные условия на открытых методах осуществляются явными проверками, которые выдают определенные, указанные исключения. Например:

/**
  * Sets the refresh rate.
  *
  * @param  rate refresh rate, in frames per second.
  * @throws IllegalArgumentException if rate <= 0 or
  * rate > MAX_REFRESH_RATE.
*/
public void setRefreshRate(int rate) {
  // Enforce specified precondition in public method
  if (rate <= 0 || rate > MAX_REFRESH_RATE)
    throw new IllegalArgumentException("Illegal rate: " + rate);
    setRefreshInterval(1000/rate);
  }

Это соглашение незатронуто добавлением assert создать. Не используйте утверждения, чтобы проверить параметры открытого метода. Утверждение является несоответствующим, потому что метод гарантирует, что это будет всегда осуществлять проверки параметра. Это должно проверить свои параметры, включаются ли утверждения. Далее, assert конструкция не выдает исключение указанного типа. Это может бросить только AssertionError.

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

/**
 * Sets the refresh interval (which must correspond to a legal frame rate).
 *
 * @param  interval refresh interval in milliseconds.
*/
 private void setRefreshInterval(int interval) {
  // Confirm adherence to precondition in nonpublic method
  assert interval > 0 && interval <= 1000/MAX_REFRESH_RATE : interval;

  ... // Set the refresh interval
 } 

Отметьте, вышеупомянутое утверждение перестанет работать если MAX_REFRESH_RATE больше чем 1000, и клиент выбирает уровень обновления, больше чем 1000. Это, фактически, указало бы на ошибку в библиотеке!

Предварительные условия состояния блокировки

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

private Object[] a;
public synchronized int find(Object key) {
  return find(key, a, 0, a.length);
}

// Recursive helper method - always called with a lock on this object
private int find(Object key, Object[] arr, int start, int len) {
 ...
} 

Вызывают статический метод holdsLock был добавлен к Thread класс теста, содержит ли текущий поток блокировку на указанном объекте. Этот метод может использоваться в комбинации с assert оператор, чтобы добавить комментарий, описывающий предварительное условие состояния блокировки, как показано в следующем примере:

// Recursive helper method - always called with a lock on this.
private int find(Object key, Object[] arr, int start, int len) {
  assert Thread.holdsLock(this); // lock-status assertion 
  ...
} 

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

Постусловия

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

 /**
  * Returns a BigInteger whose value is (this-1 mod m).
  *
  * @param  m the modulus.
  * @return this-1 mod m.
  * @throws ArithmeticException  m <= 0, or this BigInteger
  *has no multiplicative inverse mod m (that is, this BigInteger
  *is not relatively prime to m).
  */
public BigInteger modInverse(BigInteger m) {
  if (m.signum <= 0)
    throw new ArithmeticException("Modulus not positive: " + m);
  ... // Do the computation
  assert this.multiply(result).mod(m).equals(ONE) : this;
  return result;
}

Иногда необходимо сохранить некоторые данные до выполнения вычисления, чтобы проверить постусловие. Можно сделать это с два assert операторы и простой внутренний класс, который сохраняет состояние одной или более переменных, таким образом, они могут быть проверены (или перепроверены) после вычисления. Например, предположите, что у Вас есть часть кода, который похож на это:

 void foo(int[] array) {
  // Manipulate array
  ...

  // At this point, array will contain exactly the ints that it did
  // prior to manipulation, in the same order.
 }

Вот то, как Вы могли изменить вышеупомянутый метод, чтобы превратить текстовое утверждение постусловия в функциональный:

 void foo(final int[] array) {

  // Inner class that saves state and performs final consistency check
  class DataCopy {
private int[] arrayCopy;

DataCopy() { arrayCopy = (int[]) array.clone(); }

boolean isConsistent() { return Arrays.equals(array, arrayCopy); }
  }

  DataCopy copy = null;

  // Always succeeds; has side effect of saving a copy of array
  assert ((copy = new DataCopy()) != null);

  ... // Manipulate array

  // Ensure array has same ints in same order as before manipulation.
  assert copy.isConsistent();
  } 

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

Вы могли бы испытать желание заменить первый оператор контроля (который выполняется исключительно для его побочного эффекта) следующим, более выразительным оператором:

 copy = new DataCopy(); 

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

Инварианты класса

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

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

 // Returns true if this tree is properly balanced
 private boolean balanced() {
  ...
 }

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

 assert balanced(); 

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

Усовершенствованное Использование

Следующие разделы обсуждают темы, которые применяются только к ограниченным ресурсом устройствам и к системам, где утверждает, не должен быть отключен в поле. Если у Вас нет никакого интереса к этим темам, пропустите к следующему разделу, "Компилируя Файлы то Использование Утверждения".

Удаление всей Трассировки Утверждений от Файлов Класса

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

Средство утверждения не предлагает прямой поддержки разделения утверждений из файлов класса. Оператор контроля может, однако, использоваться в соединении с идиомой "условной компиляции", описанной в Спецификации языка Java, позволяя компилятору устранить все трассировки их утверждает от файлов класса, что это генерирует:

 static final boolean asserts = ... ; // false to eliminate asserts

 if (asserts)
  assert <expr> ; 

Требование, чтобы Утверждения были Включены

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

 static {
  boolean assertsEnabled = false;
  assert assertsEnabled = true; // Intentional side effect!!!
  if (!assertsEnabled)
throw new RuntimeException("Asserts must be enabled!!!");
 } 

Поместите этот статический инициализатор наверху своего класса.

Компиляция Файлов То Использование Утверждения

Для javac компилятор, чтобы принять код, содержащий утверждения, следует использовать -source 1.4 параметр командной строки как в этом примере:

 javac -source 1.4 MyClass.java 

Этот флаг необходим, чтобы не вызвать исходные проблемы совместимости.

Включение и Отключение Утверждений

По умолчанию утверждения отключаются во время выполнения. Два переключателя командной строки позволяют Вам выборочно включать или отключать утверждения.

Чтобы включить утверждениям при различных гранулярностях, используйте -enableassertions, или -ea, переключатель. Чтобы отключить утверждения при различных гранулярностях, используйте -disableassertions, или -da, переключатель. Вы определяете гранулярность с параметрами, что Вы обеспечиваете для переключателя:

Например, следующая команда выполняет программу, BatTutor, с утверждениями, включенными в только пакете com.wombat.fruitbat и его подпакеты:

 java -ea:com.wombat.fruitbat... BatTutor

Если единственная командная строка содержит многократные экземпляры этих переключателей, они обрабатываются в порядке прежде, чем загрузить любые классы. Например, следующая команда работает BatTutor программа с утверждениями, включенными в пакете com.wombat.fruitbat но отключенный в классе com.wombat.fruitbat.Brickbat:

 java -ea:com.wombat.fruitbat... -da:com.wombat.fruitbat.Brickbat BatTutor 

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

Чтобы включить утверждениям во всех системных классах, используйте различный переключатель: -enablesystemassertions, или -esa. Точно так же, чтобы отключить утверждения в системных классах, использовать -disablesystemassertions, или -dsa.

Например, следующая команда работает BatTutor программа с утверждениями, включенными в системных классах, так же как в com.wombat.fruitbat пакет и его подпакеты:

 java -esa -ea:com.wombat.fruitbat... 

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

Если оператор assert выполняется прежде, чем его класс инициализируется, выполнение должно вести себя, как будто утверждения были включены в классе. Эта тема обсуждается подробно в спецификации утверждений в Спецификации языка Java.

Совместимость С Существующими Программами

Добавление assert ключевое слово к языку программирования Java не вызывает проблем с существующими ранее двоичными файлами (.class файлы). Если Вы пытаетесь скомпилировать приложение, которое использует assert как идентификатор, однако, Вы получите предупреждающее сообщение или сообщение об ошибке. Чтобы ослабить переход от мира где assert юридический идентификатор к тому, где это не, компилятор поддерживает два режима работы в этом выпуске:

Если Вы определенно не запрашиваете исходный режим 1.4 с -source 1.4 флаг, компилятор работает в исходном режиме 1.3. Если Вы забываете использовать этот флаг, программы, которые используют новое assert оператор не будет компилировать. Наличие компилятора использует старую семантику в качестве своего поведения по умолчанию (то есть, позволяя assert использоваться в качестве идентификатора), был сделан для максимальной исходной совместимости. Исходный режим 1.3, вероятно, будет постепенно сокращен в течение долгого времени.

FAQ проекта

Вот набор часто задаваемых вопросов относительно проекта средства утверждения.

Общие Вопросы

Совместимость

Синтаксис и Семантика

Класс AssertionError

Включение и Отключение Утверждений


Oracle и/или его филиалы Авторское право © 1993, 2011, Oracle и/или его филиалы. Все права защищены.
Свяжитесь с Нами