Неизменяеми обекти в Java

1. Общ преглед

В този урок ще научим какво прави обекта неизменим, как да постигнем неизменност в Java и какви предимства идват при това.

2. Какво представлява неизменяем обект?

Неизменим обект е обект, чието вътрешно състояние остава постоянно, след като е изцяло създадено .

Това означава, че публичният API на неизменяем обект ни гарантира, че той ще се държи по същия начин през целия си живот.

Ако разгледаме String на класа , можем да видим, че дори когато неговият API изглежда ни осигурява променливо поведение с неговия метод замяна , оригиналният String не се променя:

String name = "baeldung"; String newName = name.replace("dung", "----"); assertEquals("baeldung", name); assertEquals("bael----", newName);

API ни дава методи само за четене, той никога не трябва да включва методи, които променят вътрешното състояние на обекта.

3. Последната ключова дума в Java

Преди да се опитаме да постигнем неизменност в Java, трябва да поговорим за последната ключова дума.

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

Използвайки крайната ключова дума при деклариране на променлива, компилаторът на Java няма да ни позволи да променим стойността на тази променлива. Вместо това ще докладва за грешка по време на компилация:

final String name = "baeldung"; name = "bael...";

Имайте предвид, че final ни забранява само да променяме референцията, която променливата съдържа, не ни предпазва от промяна на вътрешното състояние на обекта, към който се отнася, като използва неговия публичен API:

final List strings = new ArrayList(); assertEquals(0, strings.size()); strings.add("baeldung"); assertEquals(0, strings.size());

Вторият assertEquals ще се провали, защото добавянето на елемент към списъка променя неговия размер, следователно той не е неизменен обект.

4. Неизменност в Java

Сега, когато знаем как да избегнем промени в съдържанието на променлива, можем да я използваме за изграждане на API на неизменяеми обекти.

Изграждането на API на неизменяем обект изисква от нас да гарантираме, че вътрешното му състояние няма да се промени, независимо как използваме неговия API.

Стъпка напред в правилната посока е използването на final при деклариране на неговите атрибути:

class Money { private final double amount; private final Currency currency; // ... }

Имайте предвид, че Java ни гарантира, че стойността на сумата няма да се промени, такъв е случаят с всички променливи от примитивен тип.

В нашия пример обаче сме само гарантирани, че валутата няма да се промени, така че трябва да разчитаме на API за валута, за да се предпазим от промени .

По-голямата част от времето се нуждаем от атрибутите на обект, за да имаме персонализирани стойности, а мястото за инициализиране на вътрешното състояние на неизменяем обект е неговият конструктор:

class Money { // ... public Money(double amount, Currency currency) { this.amount = amount; this.currency = currency; } public Currency getCurrency() { return currency; } public double getAmount() { return amount; } }

Както вече казахме, за да отговори на изискванията на неизменяем API, нашият клас Money има само методи за четене.

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

5. Ползи

Тъй като вътрешното състояние на неизменяем обект остава постоянно във времето, можем да го споделим безопасно между множество нишки .

Можем също да го използваме свободно и нито един от обектите, които го реферират, няма да забележи никаква разлика, можем да кажем, че неизменяемите обекти са без странични ефекти .

6. Заключение

Неизменяемите обекти не променят вътрешното си състояние във времето, те са безопасни за нишки и без странични ефекти. Поради тези свойства неизменяемите обекти също са особено полезни при работа с многонишкови среди.

Можете да намерите примерите, използвани в тази статия, в GitHub.