Узнайте о том, как создать неизменяемый класс в java и его преимуществах.

Оглавление

  1. Обзор
  2. Использование неизменяемого класса
  3. Шаги по созданию неизменяемого класса
  4. Изучение существующих неизменяемых классов в Java
  5. Примеры с / без Mutable / Immutable class
  6. "Заключение"

1. Обзор

Неизменяемый класс, после создания экземпляра объекта мы не можем изменять его содержимое. В Java все классы-оболочки (скажем, Integer, Short, Boolean, Byte и т. Д.), String и т. Д. Классы - неизменяемы. Мы также можем создать наш собственный неизменяемый класс.

Объект является неизменным, если его состояние не может быть изменено после создания. Неизменяемые объекты не предоставляют другим объектам возможности изменять свое состояние; поля объекта инициализируются внутри конструктора только один раз и больше никогда не изменяются. [³]

Всегда помни об этом,

  • Примитивные типы данных: байтовые, короткие, целые, длинные, с плавающей запятой, двойные, логические и символьные.
  • Непримитивные типы данных, такие как строки, массивы, интерфейсы и классы.

В этой статье мы последовательно объясним типичные шаги по созданию неизменяемого класса с примерами и их использованием.

2. Использование неизменяемого класса

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

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

Что можно делать с неизменяемыми классами?

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

3. Шаги по созданию неизменяемого класса

  1. класс должен быть объявлен как final (последний класс не может быть унаследован).
  2. Члены данных в классе должны быть объявлены как частные (это позволит избежать прямого доступа).
  3. Элементы данных в классе должны быть объявлены как final (никто не может изменить его значение при создании или создании объекта).
  4. параметризованный конструктор должен инициализировать все поля, выполняющие глубокую копию (чтобы члены данных не могли быть изменены с помощью ссылки на объект)
  5. Глубокая копия объектов должна выполняться в методах получения (чтобы вернуть копию, а не фактическую ссылку на объект)
  6. Без сеттеров. Не добавляйте какие-либо сеттеры (чтобы не было возможности изменять значение переменной экземпляра)
  7. При предоставлении методов, изменяющих состояние класса, вы всегда должны возвращать новый экземпляр класса.
  8. Если класс содержит изменяемый объект (ы):
  • Внутри конструктора убедитесь, что вы используете клонированную копию переданного аргумента, и никогда не устанавливайте в изменяемом поле реальный экземпляр, переданный через конструктор, это необходимо, чтобы клиенты, которые передают объект, не изменяя его позже.
  • Обязательно всегда возвращайте клонированную копию поля и никогда не возвращайте реальный экземпляр объекта, как указано в шаге 7.

4. Изучение существующих неизменяемых классов в Java

Вы всегда слышали об хорошо известном неизменяемом классе String. После инициализации его значение не может быть изменено. Такие операции, как trim(), substring(), replace() всегда возвращают новый экземпляр и не влияют на текущий, поэтому мы обычно вызываем trim() следующим образом:

String str = "Rax";
str = str.trim();

Классы-оболочки, созданные для совместимости с примитивными типами данных, также являются примерами неизменяемых классов.

Integer, Float, Boolean и т. д. - эти классы не изменяют свое состояние, однако они создают новый экземпляр каждый раз, когда вы пытаетесь их изменить.

Float x = 1.5;
x += 5.5;

После вызова x + = 5.5 создается новый экземпляр, содержащий значение: 7, а первый экземпляр теряется.

Если вы знаете о других неизменяемых классах, не стесняйтесь писать ниже в разделе комментариев.

5. Примеры с / без изменяемого / неизменяемого класса

Давайте сначала рассмотрим простые и легкие примеры.

1. Простой неизменяемый класс

public final class ImmutableEmployee {
  private final int id;
  private final String name;
  //Parameterized Constructor
  public ImmutableEmployee(int id, String name) {
    this.name = name;
    this.id = id;
  }
  public int getId() {
    return id;
  }
  public String getName() {
    return name;
  }
}

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

2. Передача изменяемых объектов неизменяемому классу

Мы создаем изменяемый класс под названием Age и добавляем его как поле в ImmutableEmployee:

Age.java

public class Age {
private int day;
    private int month;
    private int year;
public int getDay() {
        return day;
    }
public void setDay(int day) {
    this.day = day;
    }
public int getMonth() {
    return month;
    }
public void setMonth(int month) {
    this.month = month;
    }
public int getYear() {
    return year;
    }
public void setYear(int year) {
    this.year = year;
    }
}

ImmutableEmployee.java

public final class ImmutableEmployee {
private final int id;
  private final String name;
  private final Age age;
public ImmutableEmployee(int id, String name, Age age) {
    this.name = name;
    this.id = id;
    this.age = age;
  }
public int getId() {
    return id;
  }
public String getName() {
    return name;
  }
public Age getAge() {
    return age;
  }
}

Если вы вызовете основной метод и проверите, является ли ваш класс ImmutableEmployee неизменным или нет?

public static void main(String[] args) {
Age age = new Age();
  age.setDay(1);
  age.setMonth(1);
  age.setYear(1994);
  ImmutableEmployee emp = new ImmutableEmployee(1, "Rax", age);
System.out.println("Rax age year before modification = " + emp.getAge().getYear());
age.setYear(1995);
System.out.println("Rax age year after modification = " + emp.getAge().getYear());
}

Вывод:

Rax age year before modification = 1994
Rax age year after modification = 1995

Понять, что только что произошло? - Позвольте мне уточнить!

Мы утверждаем, что ImmutableEmployee является immutable class , состояние которого никогда не изменяется после создания, однако в приведенном выше примере мы можем изменить возраст Rax даже после создания объекта Rax. Если вы проверите реализацию вышеуказанного конструктора ImmutableEmployee, вы увидите, что поле age присваивается экземпляру аргумента Age, поэтому всякий раз, когда указанный Age изменяется вне класса, это изменение отражается непосредственно на состоянии Rax немедленно. Ознакомьтесь с концепцией Передавать по значению ИЛИ передавать по ссылке, чтобы лучше понять эту концепцию.

Как можно исправить такую ​​ситуацию?

Как было сказано выше, на шаге 8 создания неизменяемых объектов экземпляр clone будет обрабатывать все за вас.

public ImmutableEmployee(int id, String name, Age age) {
    this.name = name;
    this.id = id;
    Age cloneAge = new Age();
    cloneAge.setDay(age.getDay());
    cloneAge.setMonth(age.getMonth());
    cloneAge.setYear(age.getYear());
    this.age = cloneAge;
}

Выход:

Rax age year before modification = 1994
Rax age year after modification = 1994

Но это еще не идеальный неизменяемый класс! Посмотрите в приведенном ниже примере, как сделать из него идеальный неизменяемый класс.

3. Возврат изменяемых объектов из неизменяемого класса

Согласно шагу 7, при возврате изменяемых полей из неизменяемых объектов вы должны возвращать их клонированный экземпляр, а не реальный экземпляр поля.

Чтобы изменить getAge () для возврата клона возраста объекта:

public Age getAge() {
    Age cloneAge = new Age();
    cloneAge.setDay(this.age.getDay());
    cloneAge.setMonth(this.age.getMonth());
    cloneAge.setYear(this.age.getYear());
return cloneAge;
}

Он вернет вам идеальный результат для вашего неизменяемого класса.

Rax age year before modification = 1994
Rax age year after modification = 1994

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

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

Преимущества:

  • Это дает преимущество для многопоточной среды

Недостатки:

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

Ссылка:

[1] Java ™: полный справочник Герберт Шильдт,

[2] Java First Head Book,

[3] DZone

Первоначально опубликовано Ракшитом Шахом в Неизменяемом классе BeingCoders.