Что такое инвариант класса в Java?

Я погуглил по этой теме, но, кроме Википедии, я не нашел никакой другой полезной документации или статей.

Может ли кто-нибудь простыми словами объяснить мне, что это означает, или сослаться на какую-нибудь приятную и легкую для понимания документацию?


person Saurabh Kumar    schedule 17.01.2012    source источник
comment
+1 за вопрос, потому что на странице Википедии есть действительно отличный пример того, что я не знал, что вы можете сделать - там даже есть примеры. Их объяснение лучше, чем я мог бы сделать для вас; это довольно просто.   -  person iandisme    schedule 18.01.2012
comment
stanford.edu/~pgbovine/programming-with-rep-invariants. htm   -  person Savino Sguera    schedule 18.01.2012
comment
Если вас интересуют инварианты w.r.t. Java. Возможно, вас заинтересуют контракты для Java .   -  person Mike Samuel    schedule 18.01.2012


Ответы (3)


Это ничего особенного не означает применительно к Java.

Инвариант класса - это просто свойство, которое сохраняется для всех экземпляров класса всегда, независимо от того, что делает другой код.

Например,

class X {
  final Y y = new Y();
}

X имеет инвариант класса, что есть свойство y, и никогда не null, и имеет значение типа Y.

class Counter {
  private int x;

  public int count() { return x++; }
}

Это не поддерживает два важных инварианта:

  1. Этот count никогда не возвращает отрицательное значение из-за возможного потери значимости.
  2. Количество обращений к count строго монотонно увеличивается.

Модифицированный класс сохраняет эти два инварианта.

class Counter {
  private int x;

  public synchronized int count() {
    if (x == Integer.MAX_VALUE) { throw new IllegalStateException(); }
    return x++;
  }
}

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

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

  1. Классы Java постоянно имеют или не имеют свойств и методов, поэтому инварианты интерфейса легко поддерживать.
  2. Классы Java могут защищать свои private поля, поэтому инварианты, полагающиеся на частные данные, легко поддерживать.
  3. Классы Java могут быть окончательными, поэтому инварианты, которые полагаются на отсутствие кода, нарушающего инвариант путем создания вредоносного подкласса, могут поддерживаться.
  4. Java позволяет null значениям проникать разными способами, поэтому сложно поддерживать инварианты реальных значений.
  5. В Java есть потоки, что означает, что классы, которые не синхронизируются, имеют проблемы с поддержанием инвариантов, которые полагаются на последовательные операции в потоке, происходящие вместе.
  6. В Java есть исключения, которые упрощают поддержание инвариантов, таких как возврат результата со свойством p или отсутствие результата, но сложнее поддерживать инварианты, например, всегда возвращает результат.

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

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

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

Для некоторых систем наш TCB может включать только части системы, поэтому мы не можем предполагать, что

  • Администратор или привилегированный демон не убьет наш процесс JVM,

... но мы можем предположить, что:

  • Мы можем подключиться к надежной транзакционной файловой системе.

Чем выше уровень системы, тем больше обычно ее TCB, но чем более ненадежные вещи вы можете получить от своего TCB, тем больше вероятность того, что ваши инварианты будут соблюдаться, и тем более надежной будет ваша система в долгосрочной перспективе.

person Mike Samuel    schedule 17.01.2012
comment
Неужели count никогда не возвращает одно и то же значение дважды, что действительно считается инвариантом класса? - person ruakh; 18.01.2012
comment
@ruakh, это хороший вопрос. Я не совсем уверен. Такие вещи, как стабильность hashCode (для каждого экземпляра i, i.hashCode () не изменяется) часто называют инвариантами класса, что требует рассуждений о значениях, возвращенных ранее, поэтому кажется разумным сказать, что для каждого экземпляра i i.count () не in (предыдущие результаты i.count ()) - инвариант класса. - person Mike Samuel; 18.01.2012
comment
@ruakh Разве это не чистое определение? Если я постулирую такой инвариант, почему бы и нет? Это, безусловно, может быть интересной и важной гарантией (скажем, для создания уникальных идентификаторов). Лично я также думаю, что если только однопоточный код обращается к этому классу, следующие свойства будут полезны, но я не уверен, можно ли расширить определение таким образом, чтобы оно сохранялось только при определенных условиях. истинный. (А с учетом рефлексии иначе гарантировать что-то интересное в принципе невозможно!) - person Voo; 18.01.2012
comment
@ruakh - вы можете смоделировать его как инвариант класса или инвариант метода. В любом случае для его моделирования требуется концептуальная история предыдущих вызовов метода для конкретного объекта. Фактически, вы даже можете смоделировать это как пост-условие для метода; т.е. значение результата не возвращалось ранее. - person Stephen C; 18.01.2012
comment
@Voo: Re: Разве это не чистое определение? Разумеется, но поскольку вопрос в том, что такое «инвариант класса»? Я думаю, что определение актуально на 100%. Кажется предпочтительным, насколько это возможно, использовать четкие примеры или же явно указывать на нечеткие случаи. (Я, кстати, не особо не согласен с этим примером; я был просто удивлен им и спрашивал, чтобы убедиться.) - person ruakh; 18.01.2012
comment
@ruakh, StephenC, я думаю, все согласятся, что приоритетная очередь, которая поддерживает инвариант кучи при всех возможных комбинациях вызовов методов, поддерживает инвариант класса. Если термин «инвариант класса» уместен, когда задействовано несколько методов, почему инвариант, который сохраняется кодом только в одном методе, не должен считаться частным случаем этого? Почему не следует определять инвариант метода как инвариант класса, который можно доказать, рассуждая только об одном методе. - person Mike Samuel; 18.01.2012
comment
@MikeSamuel: Чтобы уточнить, я не был удивлен инвариантом класса в отличие от инварианта метода, я был просто удивлен инвариантом класса в отношении требования, чтобы что-то изменялось. Я привык к инвариантам класса (например, инварианту кучи), которые могут быть выражены в терминах состояния экземпляра в любой момент времени, а не инвариантам, требующим ссылки на все предыдущие состояния экземпляра. (Вы упомянули стабильность хэш-кода как контрпример к этому, но она все же кажется качественно другой как-то. Я не знаю.) - person ruakh; 18.01.2012
comment
@ruakh, было бы менее удивительно, если бы я сказал, что count () строго монотонно увеличивается. Для этого требуется только ссылка на одно предыдущее состояние. Нестрогая монотонность (с точки зрения любого заданного потока) часто является желаемым инвариантом таймеров. - person Mike Samuel; 18.01.2012
comment
@MikeSamuel: Да, мне почему-то намного больше нравится эта версия. Но, пожалуйста, не просите меня оправдывать это. :-) - person ruakh; 18.01.2012
comment
Инвариант класса - это просто свойство, которое сохраняется для всех экземпляров класса всегда, независимо от того, что делает другой код. - Неправда, Википедия права, это по экземплярам. Если это для всех случаев, я думаю, вы бы назвали это константой. - person orbfish; 02.10.2013
comment
@orbfish, похоже, вы имеете в виду Википедию, но я понятия не имею, какую часть вы цитируете, что не согласуется со мной. Почему бы вам не назвать свойство, которое всегда выполняется для конкретного экземпляра, инвариантом экземпляра? - person Mike Samuel; 02.10.2013

Инвариант означает что-то, что должно придерживаться своих условий независимо от того, какие изменения или кто-либо использует / трансформирует его. Другими словами, свойство класса всегда выполняет или удовлетворяет некоторому условию даже после преобразования с использованием общедоступных методов. Таким образом, клиент или пользователь этого класса уверены в классе и его свойствах.

Например,

  1. Условием для аргумента функции является то, что он всегда должен быть ›0 (больше нуля) или не должен быть нулевым.
  2. Свойство Minimum_account_balance класса учетной записи указывает, что оно не может опускаться ниже 100. Таким образом, все публичные функции должны соблюдать это условие и обеспечивать инвариантность класса.
  3. Зависимость между переменными на основе правил, то есть значение одной переменной зависит от другой, поэтому, если одна из них изменяется с использованием какого-либо правила исправления, другая также должна измениться. Это соотношение между двумя переменными должно быть сохранено. В противном случае инвариант нарушен.
person aab10    schedule 25.10.2013

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

Доступны аннотации, с помощью которых можно проверять свойства с помощью отражения и перехватчиков. http://docs.oracle.com/javaee/7/api/javax/validation/constraints/package-summary.html

person Usman Ismail    schedule 17.01.2012