Неизменяемый (immutable) объект — это объект, состояние которого нельзя изменить после его создания. Неизменяемые объекты полезны в многопоточных приложениях, так как они гарантируют безопасность данных и отсутствие необходимости в синхронизации. Давайте рассмотрим, как создать неизменяемый объект в Java и какие требования нужно соблюдать.
Чтобы создать неизменяемый объект, необходимо соблюдать следующие требования:
Класс должен быть объявлен как final
, чтобы предотвратить его наследование и переопределение методов.
Все поля класса должны быть объявлены как final
, чтобы их значения нельзя было изменить после инициализации.
Класс не должен содержать методов, которые изменяют состояние объекта (например, сеттеров).
Если класс содержит ссылки на изменяемые объекты (например, коллекции или массивы), необходимо обеспечить их защиту от изменений. Это можно сделать, возвращая копии этих объектов или используя неизменяемые коллекции.
Все поля должны быть инициализированы через конструктор, чтобы гарантировать, что объект находится в корректном состоянии после создания.
Рассмотрим пример создания неизменяемого класса Person
, который содержит имя, возраст и список адресов.
import java.util.Collections;
import java.util.List;
public final class Person {
private final String name;
private final int age;
private final List<String> addresses;
public Person(String name, int age, List<String> addresses) {
this.name = name;
this.age = age;
this.addresses = Collections.unmodifiableList(new ArrayList<>(addresses)); // Защита от изменений
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public List<String> getAddresses() {
return addresses; // Возвращаем неизменяемый список
}
}
Person
объявлен как final
, чтобы предотвратить наследование.name
, age
, addresses
) объявлены как final
, чтобы их значения нельзя было изменить после инициализации.addresses
используется Collections.unmodifiableList
, который возвращает неизменяемую версию списка.getName
, getAge
и getAddresses
возвращают значения полей. Метод getAddresses
возвращает неизменяемый список, чтобы предотвратить изменение списка извне.Если класс содержит изменяемые поля, такие как коллекции или массивы, необходимо обеспечить их защиту от изменений. Это можно сделать следующими способами:
public List<String> getAddresses() {
return new ArrayList<>(addresses); // Возвращаем копию списка
}
public List<String> getAddresses() {
return Collections.unmodifiableList(addresses); // Возвращаем неизменяемый список
}
Если класс содержит массив, можно возвращать копию массива или использовать Collections.unmodifiableList
для списка, созданного на основе массива.
public String[] getAddresses() {
return Arrays.copyOf(addresses, addresses.length); // Возвращаем копию массива
}
public class Main {
public static void main(String[] args) {
List<String> addresses = new ArrayList<>();
addresses.add("123 Main St");
addresses.add("456 Elm St");
Person person = new Person("Alice", 30, addresses);
System.out.println("Name: " + person.getName());
System.out.println("Age: " + person.getAge());
System.out.println("Addresses: " + person.getAddresses());
// Попытка изменить список адресов
List<String> personAddresses = person.getAddresses();
personAddresses.add("789 Oak St"); // Выбросит UnsupportedOperationException
System.out.println("Addresses after modification attempt: " + person.getAddresses());
}
}
Name: Alice
Age: 30
Addresses: [123 Main St, 456 Elm St]
Exception in thread "main" java.lang.UnsupportedOperationException
at java.base/java.util.Collections$UnmodifiableCollection.add(Collections.java:1058)
at Main.main(Main.java:15)
final
.final
.Создание неизменяемых объектов — это хорошая практика, которая помогает писать более безопасный и поддерживаемый код, особенно в многопоточных приложениях.