Как создать неизменяемые объекты в Java?

Как создать неизменяемые объекты в Java?

Какие объекты следует назвать неизменяемыми?

Если у меня есть класс со всеми статическими членами, он неизменен?

Ответ 1

Ниже приведены жесткие требования неизменяемого объекта.

  1. Сделать финал класса
  2. сделать все члены окончательными, установить их явно, в статическом блоке или в конструкторе
  3. Сделайте всех участников приватными
  4. Нет методов, которые изменяют состояние
  5. Будьте предельно осторожны, чтобы ограничить доступ к изменяемым элементам (помните, что поле может быть final но объект все еще может быть изменяемым. private final Date imStillMutable). Вы должны сделать defensive copies в этих случаях.

Причины для того, чтобы сделать final класса очень тонкими и часто упускаются из виду. Если не окончательные пользователи могут свободно расширять ваш класс, переопределять public или protected поведение, добавлять изменяемые свойства, а затем предоставлять свой подкласс в качестве замены. Объявляя final класса, вы можете быть уверены, что этого не произойдет.

Чтобы увидеть проблему в действии, рассмотрите пример ниже:

public class MyApp{

    /**
     * @param args
     */
    public static void main(String[] args){

        System.out.println("Hello World!");

        OhNoMutable mutable = new OhNoMutable(1, 2);
        ImSoImmutable immutable = mutable;

        /*
         * Ahhhh Prints out 3 just like I always wanted
         * and I can rely on this super immutable class 
         * never changing. So its thread safe and perfect
         */
        System.out.println(immutable.add());

        /* Some sneak programmer changes a mutable field on the subclass */
        mutable.field3=4;

        /*
         * Ahhh let me just print my immutable 
         * reference again because I can trust it 
         * so much.
         * 
         */
        System.out.println(immutable.add());

        /* Why is this buggy piece of crap printing 7 and not 3
           It couldn't have changed its IMMUTABLE!!!! 
         */
    }

}

/* This class adheres to all the principles of 
*  good immutable classes. All the members are private final
*  the add() method doesn't modify any state. This class is 
*  just a thing of beauty. Its only missing one thing
*  I didn't declare the class final. Let the chaos ensue
*/ 
public class ImSoImmutable{
    private final int field1;
    private final int field2;

    public ImSoImmutable(int field1, int field2){
        this.field1 = field1;
        this.field2 = field2;
    }

    public int add(){
        return field1+field2;
    }
}

/*
This class is the problem. The problem is the 
overridden method add(). Because it uses a mutable 
member it means that I can't  guarantee that all instances
of ImSoImmutable are actually immutable.
*/ 
public class OhNoMutable extends ImSoImmutable{   

    public int field3 = 0;

    public OhNoMutable(int field1, int field2){
        super(field1, field2);          
    }

    public int add(){
       return super.add()+field3;  
    }

}

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

Вывод заключается в том, что для того, чтобы получить твердые гарантии неизменности, вы должны отметить класс как final. Это подробно рассматривается в Joshua Bloch Effective Java и явно упоминается в спецификации модели памяти Java.

Ответ 2

Классы не являются неизменяемыми, объекты есть.

Неизменяемые средства: мое публичное видимое состояние не может измениться после инициализации.

Поля не обязательно должны быть объявлены окончательными, хотя это может значительно помочь обеспечить безопасность потоков

Если у класса есть только статические члены, объекты этого класса неизменяемы, потому что вы не можете изменить состояние этого объекта (вы, вероятно, не можете его создать:))

Ответ 3

Просто не добавляйте в класс методы public mutator (setter).

Ответ 4

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

1. Не предоставляйте методы настройки для изменения значений любой из переменных экземпляра класса. 2. Объявите класс как 'final'. Это предотвратило бы распространение любого другого класса и, следовательно, исключение из него любого метода, который мог бы изменять значения переменных экземпляра. 3. Объявите переменные экземпляра как private и final.

4. Вы также можете объявить конструктор класса как private и добавить метод factory для создания экземпляра класса при необходимости.

Эти точки должны помочь!

Ответ 5

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

Declare the class as final so it can’t be extended.
Make all fields private so that direct access is not allowed.
Don’t provide setter methods for variables
Make all mutable fields final so that it’s value can be assigned only once.
Initialize all the fields via a constructor performing deep copy.
Perform cloning of objects in the getter methods to return a copy rather than returning the actual object reference.

Ответ 6

От oracle сайт, как создать неизменяемые объекты в Java.

  • Не предоставляйте методы setter - методы, которые изменяют поля или объекты, на которые ссылаются поля.
  • Сделать все поля окончательными и частными.
  • Не позволяйте подклассам переопределять методы. Самый простой способ сделать это - объявить класс окончательным. Более сложный подход состоит в том, чтобы сделать конструктор частным и построить экземпляры в factory методах.
  • Если в поля экземпляра включены ссылки на изменяемые объекты, не разрешайте изменять эти объекты:
    I. Не предоставляйте методы, которые изменяют изменяемые объекты.
    II. Не используйте ссылки на изменяемые объекты. Никогда не храните ссылки на внешние, изменяемые объекты, переданные конструктору; при необходимости, создавать копии и хранить ссылки на копии. Аналогичным образом создайте копии своих внутренних изменяемых объектов, если это необходимо, чтобы избежать возврата оригиналов в ваши методы.

Ответ 7

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

Преимущества неизменяемого объекта

Concurrency и многопоточность Это автоматически Thread-safe, поэтому проблема синхронизации... и т.д.

Не нужно копировать конструктор Не требуется реализация клона. Класс не может быть переопределить Сделайте поле как частным и окончательным Принудительные вызовы для создания объекта полностью за один шаг, вместо использования конструктора no-Argument

Неизменяемые объекты - это просто объекты, состояние которых означает, что данные объекта не могут измениться после построены неизменяемый объект.

см. ниже код.

public final class ImmutableReminder{
    private final Date remindingDate;

    public ImmutableReminder (Date remindingDate) {
        if(remindingDate.getTime() < System.currentTimeMillis()){
            throw new IllegalArgumentException("Can not set reminder" +
                    " for past time: " + remindingDate);
        }
        this.remindingDate = new Date(remindingDate.getTime());
    }

    public Date getRemindingDate() {
        return (Date) remindingDate.clone();
    }
}

Ответ 8

  • Не предоставляйте методы "setter" - методы, которые изменяют поля или объекты, на которые ссылаются поля.
  • Сделать все поля окончательными и частными.
  • Не позволяйте подклассам переопределять методы. Самый простой способ сделать это - объявить класс окончательным. Более сложный подход состоит в том, чтобы сделать конструктор частным и построить экземпляры в factory методах.
  • Если поля экземпляра включают ссылки на изменяемые объекты, не разрешайте изменять эти объекты:
    • Не предоставляйте методы, изменяющие изменяемые объекты.
    • Не используйте ссылки на изменяемые объекты. Никогда не храните ссылки на внешние, изменяемые объекты, переданные конструктору; при необходимости, создавать копии и хранить ссылки на копии. Аналогичным образом создайте копии своих внутренних изменяемых объектов, если это необходимо, чтобы избежать возврата оригиналов в ваши методы.

Ответ 9

Минимизировать изменчивость

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

JDK неизменяемые классы: String, классы в штучной упаковке (классы-оболочки), BigInteger и BigDecimal и т.д.

Как сделать класс неизменным?

  • Не предоставляйте какие-либо методы, которые изменяют состояние объектов (известные как мутаторы).
  • Убедитесь, что класс не может быть расширен.
  • Сделайте все поля окончательными.
  • Сделать все поля закрытыми. Это не позволяет клиентам получить доступ к изменяемым объектам, на которые ссылаются поля, и напрямую изменять эти объекты.
  • Сделайте защитные копии. Обеспечьте эксклюзивный доступ к любым изменяемым компонентам.

    public Список getList() {    return Collections.unmodifiableList(список); < === защитная копия изменчивого                                                                             перед возвратом его вызывающему абоненту }

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

import java.util.Date;
public final class ImmutableClass {

       public ImmutableClass(int id, String name, Date doj) {
              this.id = id;
              this.name = name;
              this.doj = doj;
       }

       private final int id;
       private final String name;
       private final Date doj;

       public int getId() {
              return id;
       }
       public String getName() {
              return name;
       }

     /**
      * Date class is mutable so we need a little care here.
      * We should not return the reference of original instance variable.
      * Instead a new Date object, with content copied to it, should be returned.
      * */
       public Date getDoj() {
              return new Date(doj.getTime()); // For mutable fields
       }
}
import java.util.Date;
public class TestImmutable {
       public static void main(String[] args) {
              String name = "raj";
              int id = 1;
              Date doj = new Date();

              ImmutableClass class1 = new ImmutableClass(id, name, doj);
              ImmutableClass class2 = new ImmutableClass(id, name, doj);
      // every time will get a new reference for same object. Modification in              reference will not affect the immutability because it is temporary reference.
              Date date = class1.getDoj();
              date.setTime(date.getTime()+122435);
              System.out.println(class1.getDoj()==class2.getDoj());
       }
}

Для получения дополнительной информации см. мой блог:
http://javaexplorer03.blogspot.in/2015/07/minimize-mutability.html

Ответ 10

объект называется неизменяемым, если его состояние не может быть изменено после его создания. Один из самых простых способов создания неизменяемого класса в Java заключается в том, что все его поля являются окончательными. Если вам нужно написать неизменяемый класс, который включает в себя изменяемые классы, такие как "java.util.Date". Чтобы сохранить неизменность в таких случаях, советуется вернуть копию исходного объекта,

Ответ 11

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

Особенности неизменяемых классов:

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

Ключи для записи неизменяемого класса:

  • убедитесь, что класс нельзя переопределить.
  • сделать все переменные-члены private и final
  • не дают свои методы настройки
  • ссылка на объект не должна просачиваться на этапе строительства

Ответ 12

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

  • Класс должен быть помечен как final
  • Все поля должны быть закрытыми и конечными
  • Заменить сеттеры конструктором (для присвоения значения переменная).

Давайте взглянем на то, что мы набрали выше:

//ImmutableClass
package younus.attari;

public final class ImmutableExample {

    private final String name;
    private final String address;

    public ImmutableExample(String name,String address){
        this.name=name;
        this.address=address;
    }


    public String getName() {
        return name;
    }

    public String getAddress() {
        return address;
    }

}

//MainClass from where an ImmutableClass will be called
package younus.attari;

public class MainClass {

    public static void main(String[] args) {
        ImmutableExample example=new ImmutableExample("Muhammed", "Hyderabad");
        System.out.println(example.getName());

    }
}

Ответ 13

Обычно игнорируемые, но важные свойства неизменяемых объектов

В дополнение к ответу, предоставленному @nsfyn55, необходимо учитывать следующие аспекты неизменяемости объектов, которые имеют первостепенное значение

Рассмотрим следующие классы:

public final class ImmutableClass {

  private final MutableClass mc;

  public ImmutableClass(MutableClass mc) {
    this.mc = mc;
  }

  public MutableClass getMutClass() {
    return this.mc;
  }
}

public class MutableClass {

  private String name;

  public String getName() {
    return this.name;
  }

  public void setName(String name) {
    this.name = name;
  }
}


public class MutabilityCheck {

public static void main(String[] args) {

  MutableClass mc = new MutableClass();

  mc.setName("Foo");

  ImmutableClass iMC = new ImmutableClass(mc);

  System.out.println(iMC.getMutClass().getName());

  mc.setName("Bar");

  System.out.println(iMC.getMutClass().getName());

  }

 }

Ниже будет вывод из MutabilityCheck:

 Foo
 Bar

Важно отметить, что

  1. Построение изменяемых объектов на неизменяемом объекте (через конструктор) путем "копирования" или "закрытия" переменных экземпляра неизменяемого объекта, описанного следующими изменениями:

    public final class ImmutableClass {
    
       private final MutableClass mc;
    
       public ImmutableClass(MutableClass mc) {
         this.mc = new MutableClass(mc);
       }
    
       public MutableClass getMutClass() {
         return this.mc;
       }
    
     }
    
     public class MutableClass {
    
      private String name;
    
      public MutableClass() {
    
      }
      //copy constructor
      public MutableClass(MutableClass mc) {
        this.name = mc.getName();
      }
    
      public String getName() {
        return this.name;
      }
    
      public void setName(String name) {
       this.name = name;
      } 
     }
    

все еще не гарантирует полную неизменность, так как следующее все еще допустимо из класса MutabilityCheck:

  iMC.getMutClass().setName("Blaa");
  1. Тем не менее, запуск MutabilityCheck с изменениями, внесенными в 1., приведет к выводу:

    Foo
    Foo
    
  2. Для достижения полной неизменности объекта все его зависимые объекты также должны быть неизменными.

Ответ 14

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

Чтобы создать неизменный объект, вы должны следовать нескольким простым правилам:

1. Не добавляйте метод установки

Если вы строите неизменный объект, его внутреннее состояние никогда не изменится. Задача метода сеттера - изменить внутреннее значение поля, чтобы вы не могли его добавить.

2. Объявите все поля как окончательные и закрытые

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

Объявление поля final гарантирует, что если оно ссылается на примитивное значение, значение никогда не изменится, если оно ссылается на объект, ссылка не может быть изменена. Этого недостаточно, чтобы гарантировать, что объект с только частными конечными полями не является изменяемым.

3. Если поле является изменяемым объектом, создайте его защитные копии для методов получения

Ранее мы видели, что определения поля final и private недостаточно, поскольку можно изменить его внутреннее состояние. Чтобы решить эту проблему, нам нужно создать защитную копию этого поля и возвращать это поле каждый раз, когда оно запрашивается.

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

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

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

5. Не позволяйте подклассам переопределять методы

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

Для решения этой проблемы можно выполнить одно из следующих действий:

  1. Объявите неизменный класс как final, чтобы он не мог быть расширен
  2. Объявите все методы неизменяемого класса final, чтобы они не могли быть переопределены
  3. Создайте приватный конструктор и фабрику для создания экземпляров неизменяемого класса, потому что класс с приватными конструкторами не может быть расширен

Если вы будете следовать этим простым правилам, вы можете свободно делиться своими неизменяемыми объектами между потоками, потому что они потокобезопасны!

Ниже приведены еще несколько моментов изменчивости/неизменности:

  • Неизменяемые объекты действительно облегчают жизнь во многих случаях. Они особенно применимы для типов значений, где объекты не имеют идентичности, поэтому их можно легко заменить и они могут сделать параллельное программирование более безопасным и чистым (большинство общеизвестно трудных для обнаружения ошибок параллелизма в конечном счете вызвано изменяемым состоянием, разделяемым между потоки). Однако для больших и/или сложных объектов создание новой копии объекта для каждого отдельного изменения может быть очень дорогостоящим и/или утомительным. А для объектов с индивидуальной идентичностью изменение существующих объектов намного проще и интуитивно понятнее, чем создание новой модифицированной копии.
  • Есть некоторые вещи, которые вы просто не можете сделать с неизменяемыми объектами, например, иметь двунаправленные отношения. Как только вы устанавливаете значение ассоциации для одного объекта, его идентичность меняется. Итак, вы устанавливаете новое значение для другого объекта, и оно также изменяется. Проблема в том, что первая ссылка на объект больше не действительна, потому что был создан новый экземпляр для представления объекта со ссылкой. Продолжение этого приведет к бесконечным регрессам.
  • Чтобы реализовать двоичное дерево поиска, вы должны каждый раз возвращать новое дерево: вашему новому дереву придется делать копию каждого измененного узла (неизмененные ветки являются общими). Для вашей функции вставки это не так уж плохо, но для меня все стало довольно неэффективно, когда я начал работать над удалением и перебалансировкой.
  • Hibernate и JPA по сути диктуют, что ваша система использует изменяемые объекты, потому что вся их предпосылка в том, что они обнаруживают и сохраняют изменения в ваших объектах данных.
  • В зависимости от языка компилятор может сделать кучу оптимизаций при работе с неизменяемыми данными, потому что он знает, что данные никогда не изменятся. Все виды вещей пропускаются, что дает вам огромный выигрыш в производительности.
  • Если вы посмотрите на другие известные языки JVM (Scala, Clojure), изменяемые объекты редко встречаются в коде, и поэтому люди начинают использовать их в сценариях, где однопоточности недостаточно.

Там нет правильного или неправильного, это просто зависит от того, что вы предпочитаете. Это зависит только от ваших предпочтений и от того, чего вы хотите достичь (и возможность легко использовать оба подхода, не отталкивая непоколебимых поклонников той или иной стороны, является священным Граалем, к которому стремятся некоторые языки).