2015-12-05 3 views
2

Я знаю все основные правила, чтобы сделать наш класс неизменным, но я немного смущен, когда есть еще одна ссылка на класс. Я знаю, есть ли коллекция вместо Address, тогда мы сможем использовать Collections.unmodifiableList(new ArrayList<>(modifiable));, а затем можем сделать наш класс неизменным. Но в следующем случае я все еще не могу получить концепцию.Как мы можем сохранить неизменность класса с изменчивой ссылкой

public final class Employee{ 
    private final int id; 
    private Address address; 
    public Employee(int id, Address address) 
    { 
     this.id = id; 
     this.address=address; 
    } 
    public int getId(){ 
     return id; 
    } 
    public Address getAddress(){ 
     return address; 
    } 
} 

public class Address{ 
    private String street; 
    public String getStreet(){ 
     return street; 
    } 
    public void setStreet(String street){ 
     this.street = street; 
    } 
} 
+0

'Employee' не является неизменным, и там не так много вы можете сделать, чтобы изменить это, если вы не можете изменить' Address' класс для поддержки при минимальном копировании, но предпочтительно сделать «адрес» неизменным в первую очередь. –

ответ

3

Ну, концепция читает JLS и понимает ее. В этом случае JLS говорит:

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

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

Так что вам нужно:

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

В этом случае # 2, вероятно, означает, что вы не можете вернуть ссылку на адрес, как у вас, с getAddress(). И вам нужно сделать защитную копию. I.e., сделайте копию объекта и сохраните копию в Employee. Если вы не можете сделать оборонительную копию, то действительно невозможно сделать Работника неизменным.

public final class Employee{ 
    private final int id; 
    private final Address address; 
    public Employee(int id, Address address) 
    { 
     this.id = id; 
     this.address=new Address(); // defensive copy 
     this.address.setStreet(address.getStreet()); 
    } 
    pulbic int getId(){ 
     return id; 
    } 
    public Address getAddress() { 
     Address nuAdd = new Address(); // must copy here too 
     nuAdd.setStreet(address.getStreet()); 
     return nuAdd; 
} 

Реализация clone() или что-то подобное (копия CTOR) сделает создание оборонительных объектов проще для сложных классов. Тем не менее, лучшая рекомендация, я думаю, будет заключаться в том, чтобы сделать Address неизменяемым. Как только вы это сделаете, вы сможете свободно обходить свою ссылку без каких-либо проблем с обеспечением безопасности потоков.

В этом примере обратите внимание, что я делаю NOT необходимо скопировать значение street. Street - это строка, и строки неизменяемы.Если street состоял из изменчивых полей (например, число целых улиц), то I было бы также сделать копию street также и так далее до бесконечности. Вот почему неизменяемые объекты настолько ценны, что они разрушают цепочку «бесконечной копии».

+0

отметьте, не могли бы вы рассказать мне, как сделать защитную копию адреса. пример был бы очень полезен – user1111880

+0

отличная оценка, я каким-то образом получил концепцию, но поскольку мы уже создали объект Address в конструкторе Employee(), то почему мы создаем его снова в методе getAdress() – user1111880

+0

Я сказал, почему, по крайней мере, дважды. JLS говорит, что объект Employee не является неизменным, если мы не предотвращаем просмотр внутренних ссылок. Если мы используем значение, которое было передано в ctor, тогда кто-то может держать эту ссылку. Следовательно, мы не можем использовать его, мы должны сделать свой собственный объект, который никто не может видеть. – markspace

0

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

Address класс mutable, потому что вы можете изменить его с помощью метода setStreet.

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

4

Ну есть шаги, предусмотренные Java docs

A Стратегии для определения неизменных объектов

Следующие правила определяют простую стратегию для создания неизменные объектов. Не все классы, задокументированные как «неизменные», следуют этим правилам. Это не обязательно означает, что создатели этих классов были sloppy - у них могут быть веские основания полагать, что экземпляры их классы никогда не меняются после строительства. Однако такие стратегии требуют сложного анализа и не предназначены для новичков.

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

Адрес класс изменчив, потому что вы можете изменить его с помощью методы setStreet. Так что другой класс может изменить этот класс.

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

Создание Адрес объекта окончательного

private final Address address; 

Во-вторых,

this.address = new Address(address.getStreet()); 

Создать конструктор в классе Address, который устанавливает метод установки Street.Remove для улицы.

И, наконец, вместо

public Address getAddress(){ 
    return address; 
} 

Использование

public Address getAddress(){ 
    return new Address(address.getStreet()); 
} 
+0

Да, я знаю, но как мы можем реализовать последние 2 пункта в моем случае. можете ли вы сказать мне, что – user1111880

+0

@ user1111880 Я обновил ответ ... См. http://www.javaranch.com/journal/2003/04/immutable.htm также – Naruto

+0

@ user1111880 Пожалуйста, отметьте решение как ПРИНЯТО, если ваша проблема . Таким образом, это поможет другим, кто ищет аналогичную проблему – Naruto

0

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

  1. Создать a копия изменяемого объекта (т.е. через конструктор копирования, клонирование, сериализацию/десериализацию и т. д.); никогда не хранит ссылку на исходный изменяемый объект.
  2. Никогда не возвращайте изменяемый объект. Если вам нужно, то верните копию объекта.
  3. Избегайте методов, которые могут изменить изменяемый объект.

государственный служащий (интермедиат идентификатор, адрес и адрес) {

 this.id = id; 
     this.address=new Address(); 
     this.address.setStreet(address.getStreet()); 
    } 



public Address getAddress() { 
     Address nuAdd = new Address(); // must copy here too 
     nuAdd.setStreet(address.getStreet()); 
     return nuAdd; 
} 
Смежные вопросы