Как создать неизменяемые объекты в Java? - PullRequest
80 голосов
/ 10 июня 2011

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

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

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

Ответы [ 13 ]

82 голосов
/ 10 июня 2011

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

  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.Это подробно рассматривается в Effective Java Джошуа Блоха и явно упоминается в спецификации для модели памяти Java .

.
14 голосов
/ 10 июня 2011

Только не добавляйте публичные методы-мутаторы (setter) в класс.

13 голосов
/ 10 июня 2011

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

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

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

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

6 голосов
/ 31 декабря 2012

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

1. Не предоставляйте методы установки для изменения значений любой из переменных экземпляраclass.

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

3. Объявите переменные экземпляра как private и final .

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

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

3 голосов
/ 21 июня 2016

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

  1. Не предоставляйте "установочные" методы - методы, которые изменяют поля или объекты, на которые ссылаются поля.
  2. Сделайте все поля окончательными и приватными.
  3. Не разрешать подклассам переопределять методы. Самый простой способ сделать это - объявить класс как final. Более сложный подход - сделать конструктор частным и создать экземпляры в фабричных методах.
  4. Если поля экземпляра содержат ссылки на изменяемые объекты, не допускайте изменения этих объектов:
    I. Не предоставляйте методы, которые изменяют изменяемые объекты.
    II. Не делитесь ссылками на изменяемые объекты. Никогда не храните ссылки на внешние изменяемые объекты, переданные в конструктор; при необходимости создайте копии и сохраните ссылки на копии. Аналогичным образом, при необходимости создайте копии ваших внутренних изменяемых объектов, чтобы избежать возврата оригиналов в ваши методы.
2 голосов
/ 27 января 2019

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

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

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

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

2.Объявите все поля final и private

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

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

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

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

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

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

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

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

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

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

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

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

Ниже приведено несколько заметных моментов:

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

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

2 голосов
/ 24 декабря 2016

Минимизация изменчивости

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

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

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

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

    публичный список getList () { return Collections.unmodifiableList (список); <=== защитная копия изменяемой поле, прежде чем вернуть его вызывающему } </p>

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

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

2 голосов
/ 11 июня 2015
  • Не предоставлять "установочные" методы - методы, которые изменяют поля или объекты, на которые ссылаются поля.
  • Делать все поля окончательными и приватными.
  • Не разрешать подклассыпереопределить методы.Самый простой способ сделать это - объявить класс как final.Более сложный подход - сделать конструктор частным и создать экземпляры в фабричных методах.
  • Если поля экземпляра содержат ссылки на изменяемые объекты, не разрешайте изменять эти объекты:
    • Донне предоставляют методы, которые изменяют изменяемые объекты.
    • Не делитесь ссылками на изменяемые объекты.Никогда не храните ссылки на внешние изменяемые объекты, переданные в конструктор;при необходимости создайте копии и сохраните ссылки на копии.Аналогично, при необходимости создайте копии ваших внутренних изменяемых объектов, чтобы избежать возврата оригиналов в ваши методы.
2 голосов
/ 03 января 2014

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

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

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

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

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

см. Код ниже.

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();
    }
}
1 голос
/ 17 января 2014

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

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

Давайте посмотрим, что мы напечатали выше:

//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());

    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...