Що таке інваріант класу в Java?


93

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

Хто-небудь може пояснити мені простими словами, що це означає, або передати мені якусь приємну та зрозумілу документацію?


2
+1 за запитання, оскільки на сторінці Вікіпедії є справді чудовий приклад того, що я не знав, що ви можете зробити, - навіть є приклади. Їх пояснення краще, ніж я міг би зробити для вас; це досить просто.
iandisme


Якщо вас цікавлять інваріанти з Java, можливо, вас зацікавлять контракти на Java .
Mike Samuel

1
Більш просте пояснення - < stackoverflow.com/questions/112064/what-is-an-invariant?rq=1 >
ip_x

Відповіді:


91

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


1
Чи countсправді "що ніколи не повертає одне і те ж значення двічі" вважається класом інваріантом?
ruahh

@ruakh, це хороше питання. Я не зовсім впевнений. Такі речі, як стабільність hashCode (для кожного екземпляра i, i.hashCode () не змінюється) часто називають інваріантами класу, що вимагає міркувань щодо значень, повернутих раніше, тому представляється розумним сказати, що "для кожного екземпляра i, i.count () not in (попередні результати i.count ()) "є інваріантом класу.
Mike Samuel

@ruakh Хіба це не чисте визначення? Якщо я постулюю такий інваріант, чому б і ні? Це, безумовно, може бути цікавою та важливою гарантією (скажімо, для створення унікальних посвідчень). Особисто я також думаю, що щось на кшталт "якщо до цього класу звертається лише однопотоковий код, будуть утримуватися наступні властивості", але я не впевнений, чи можливо розширити визначення таким чином, що воно має містити лише певні умови істинні. (А враховуючи роздуми, в принципі неможливо гарантувати що-небудь цікаве інакше!)
Voo

1
@ruakh - ви можете змоделювати його як інваріант класу або інваріант методу. У будь-якому випадку, для його моделювання потрібна концептуальна історія попередніх викликів методу для конкретного об'єкта. Насправді, ви можете навіть змоделювати це як пост-умову методу; тобто значення результату - це те, яке раніше не поверталось.
Stephen C

@Voo: Re: "Хіба це не чисто поняття?": Звичайно, але оскільки тут виникає запитання: "Що таке" інваріант класу "?", Я думаю, що визначення є на 100% актуальним. Здається, бажано, наскільки це можливо, використовувати чіткі приклади або ж явно називати випадки нечіткої чіткості. (Я, до речі, активно не погоджуюся з прикладом; я просто був здивований цим і просив, щоб переконатися.)
ruahh

20

Інваріант означає щось, що повинно дотримуватися своїх умов, незалежно від змін і того, хто його використовує / трансформує. Тобто властивість класу завжди виконує або задовольняє якусь умову навіть після перетворення за допомогою відкритих методів. Отже, клієнт або користувач цього класу має впевненість щодо класу та його властивостей.

Наприклад,

  1. умова аргументу функції полягає в тому, що вона завжди повинна бути> 0 (більша за нуль) або не повинна бути нульовою.
  2. Властивість minimum_account_balance класу акаунта стверджує, воно не може бути нижче 100. Отже, всі публічні функції повинні поважати цю умову та забезпечувати інваріант класу.
  3. залежність на основі правил між змінними, тобто значення однієї змінної залежить від іншої, тому, якщо одна змінюється, використовуючи якесь правило виправлення, повинна змінюватися і інша. Цей зв’язок між 2 змінними повинен бути збережений. Якщо цього не відбувається, то інваріант порушується.

11

Це факти, які мають бути правдивими щодо класу екземпляра. Наприклад, якщо клас має властивість X, а інваріант може бути X, має бути більше 0. Наскільки мені відомо, немає вбудованого методу для підтримки інваріантів, ви повинні зробити властивості приватними та переконатися, що ваші геттери та сетери встановлюють властивість інваріантності.

Доступні анотації, які можуть перевіряти властивості за допомогою відбиття та перехоплювачів. http://docs.oracle.com/javaee/7/api/javax/validation/constraints/package-summary.html

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.