Русский
Русский
English
Статистика
Реклама

Перевод Иммутабельность в Java

Привет, Хабр. В преддверии скорого старта курса Подготовка к сертификации Oracle Java Programmer (OCAJP) подготовили для вас традиционный перевод материала.

Приглашаем также всех желающих поучаствовать в открытом демо-уроке Конструкторы и блоки инициализации. На этом бесплатном вебинаре мы:
- Разберём конструктор на запчасти
- Определим финалистов (финальные переменные)
- Наведём порядок (инициализации)


Иммутабельный (неизменяемый, immutable) класс это класс, который после инициализации не может изменить свое состояние. То есть если в коде есть ссылка на экземпляр иммутабельного класса, то любые изменения в нем приводят к созданию нового экземпляра.

Чтобы класс был иммутабельным, он должен соответствовать следующим требованиям:

  • Должен быть объявлен как final, чтобы от него нельзя было наследоваться. Иначе дочерние классы могут нарушить иммутабельность.

  • Все поля класса должны быть приватными в соответствии с принципами инкапсуляции.

  • Для корректного создания экземпляра в нем должны быть параметризованные конструкторы, через которые осуществляется первоначальная инициализация полей класса.

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

  • Для полей-коллекций необходимо делать глубокие копии, чтобы гарантировать их неизменность.

Иммутабельность в действии

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

import java.util.Map;public final class MutableClass {  private String field;  private Map<String, String> fieldMap;public MutableClass(String field, Map<String, String> fieldMap) {  this.field = field;  this.fieldMap = fieldMap;}public String getField() {  return field;}public Map<String, String> getFieldMap() {  return fieldMap;}}

Теперь посмотрим на него в действии.

import java.util.HashMap;import java.util.Map;public class App {public static void main(String[] args) {  Map<String, String> map = new HashMap<>();  map.put("key", "value");      // Инициализация нашего "иммутабельного" класса  MutableClass mutable = new MutableClass("this is not immutable", map);  // Можно легко добавлять элементы в map == изменение состояния  mutable.getFieldMap().put("unwanted key", "another value");  mutable.getFieldMap().keySet().forEach(e ->  System.out.println(e));}}// Вывод в консолиunwanted keykey

Очевидно, что мы хотим запретить добавление элементов в коллекцию, поскольку это изменение состояния объекта, то есть отсутствие иммутабельности.

import java.util.HashMap;import java.util.Map;public class AlmostMutableClass {  private String field;  private Map<String, String> fieldMap;public AlmostMutableClass(String field, Map<String, String> fieldMap) {  this.field = field;  this.fieldMap = fieldMap;}public String getField() {  return field;}public Map<String, String> getFieldMap() {  Map<String, String> deepCopy = new HashMap<String, String>();  for(String key : fieldMap.keySet()) {    deepCopy.put(key, fieldMap.get(key));  }  return deepCopy;}}

Здесь мы изменили метод getFieldMap, который теперь возвращает глубокую копию коллекции, ссылка на которую есть в AlmostMutableClass. Получается, что если мы получим Map, вызвав метод getFieldMap, и добавим к нему элемент, то на map из нашего класса это никак не повлияет. Изменится только map, которую мы получили.

Однако если у нас остается доступ к исходной map, которая была передана в качестве параметра конструктору, то все не так уж и хорошо. Мы можем изменить ее, тем самым изменив состояние объекта.

import java.util.HashMap;import java.util.Map;public class App {public static void main(String[] args) {  Map<String, String> map = new HashMap<>();  map.put("good key", "value");      // Инициализация нашего "иммутабельного" класса  AlmostMutableClass almostMutable = new AlmostMutableClass("this is not immutable", map);    // Мы не можем изменять состояние объекта   // через добавление элементов в полученную map  System.out.println("Result after modifying the map after we get it from the object");  almostMutable.getFieldMap().put("bad key", "another value");  almostMutable.getFieldMap().keySet().forEach(e -> System.out.println(e));    System.out.println("Result of the object's map after modifying the initial map");  map.put("bad key", "another value");  almostMutable.getFieldMap().keySet().forEach(e -> System.out.println(e));      }}// Вывод в консолиResult after modifying the map after we get it from the objectgood keyResult of the object's map after modifying the initial mapgood keybad key

Мы забыли, что в конструкторе нужно сделать то же самое, что и в методе getFieldMap. В итоге конструктор должен выглядеть так:

public AlmostMutableClass(String field, Map<String, String> fieldMap) {  this.field = field;        Map<String, String> deepCopy = new HashMap<String, String>();  for(String key : fieldMap.keySet()) {    deepCopy.put(key, fieldMap.get(key));  }  this.fieldMap = deepCopy;}// Вывод в консолиResult after modifying the map after we get it from the objectgood keyResult of the object's map after modifying the initial mapgood key

Хотя использование иммутабельных объектов дает преимущества, но их использование не всегда оправдано. Обычно нам нужно как создавать объекты, так и модифицировать их для отражения изменений, происходящих в системе.

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

Иммутабельность строк в Java

Класс String, представляющий набор символов, вероятно, самый популярный класс в Java. Его назначение упростить работу со строками, предоставляя различные методы для их обработки.

Например, в классе String есть методы для получения символов, выделения подстрок, поиска, замены и многие другие. Как и другие классы-обертки в Java (Integer, Boolean и т.д.), класс String является иммутабельным.

Иммутабельность строк дает следующие преимущества:

  • Строки потокобезопасны.

  • Для строк можно использовать специальную область памяти, называемую "пул строк". Благодаря которой две разные переменные типа String с одинаковым значением будут указывать на одну и ту же область памяти.

  • Строки отличный кандидат для ключей в коллекциях, поскольку они не могут быть изменены по ошибке.

  • Класс String кэширует хэш-код, что улучшает производительность хеш-коллекций, использующих String.

  • Чувствительные данные, такие как имена пользователей и пароли, нельзя изменить по ошибке во время выполнения, даже при передаче ссылок на них между разными методами.


Подробнее о курсе Подготовка к сертификации Oracle Java Programmer (OCAJP).

Смотреть вебинар Конструкторы и блоки инициализации.

Источник: habr.com
К списку статей
Опубликовано: 15.04.2021 20:09:13
0

Сейчас читают

Комментариев (0)
Имя
Электронная почта

Блог компании otus

Oracle

Java

String

Ocajp

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru