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

94

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

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

Саураб Кумар
источник
2
+1 за вопрос, потому что на странице Википедии есть действительно отличный пример того, что я не знал, что вы можете сделать - там даже есть примеры. Их объяснение лучше, чем я мог бы сделать для вас; это довольно просто.
iandisme
Если вас интересуют инварианты Java, возможно, вас заинтересуют контракты для Java .
Майк Сэмюэл
1
Более простое объяснение - < stackoverflow.com/questions/112064/what-is-an-invariant?rq=1 >
ip_x

Ответы:

93

Это ничего особенного не означает применительно к 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, тем больше вероятность того, что ваши инварианты будут соблюдаться, и тем надежнее будет ваша система в долгосрочной перспективе.

Майк Сэмюэл
источник
2
Действительно ли "это countникогда не возвращает одно и то же значение дважды" считается инвариантом класса?
ruakh
1
@ruakh, это хороший вопрос. Я не совсем уверен. Такие вещи, как стабильность hashCode (для каждого экземпляра i, i.hashCode () не изменяется) часто называют инвариантами класса, что требует рассуждений о значениях, возвращенных ранее, поэтому кажется разумным сказать, что «для каждого экземпляра i, i.count () not in (предыдущие результаты i.count ()) "инвариант класса.
Майк Сэмюэл
@ruakh Разве это не чистое определение? Если я постулирую такой инвариант, почему бы и нет? Это, безусловно, может быть интересной и важной гарантией (например, для создания уникальных идентификаторов). Лично я также думаю, что что-то вроде «если только однопоточный код обращается к этому классу, следующие свойства будут сохраняться», полезно, но я не уверен, можно ли расширить определение таким образом, чтобы оно сохранялось только в определенных условия верны. (А учитывая отражение, в противном случае невозможно гарантировать что-либо интересное!)
Voo
1
@ruakh - вы можете смоделировать его как инвариант класса или инвариант метода. В любом случае для его моделирования требуется концептуальная история предыдущих вызовов метода для конкретного объекта. Фактически, вы даже можете смоделировать это как пост-условие для метода; т.е. значение результата не возвращалось ранее.
Stephen C
@Voo: Re: «Разве это не чистое определение?»: Конечно, но поскольку вопрос здесь: «Что такое« инвариант класса »?», Я думаю, что определение актуально на 100%. Кажется предпочтительным, насколько это возможно, использовать четкие примеры или же явно указывать на случаи нечеткости. (Я, кстати, не
особо
21

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

Например,

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

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

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

Усман Исмаил
источник