Подтвердить что ты не робот

Должны ли все свойства неизменяемого объекта быть окончательными?

Должен ли неизменный объект иметь все свойства final?

По словам меня нет. Но я не знаю, правильно ли я.

4b9b3361

Ответ 1

Основное отличие между неизменяемым объектом (все свойства final) и эффективно неизменяемым объектом (свойства не являются окончательными, но не могут быть изменены) - это безопасная публикация.

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

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

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


Разъяснения

  • Создание всех полей конечного объекта не делает его неизменным - вам также необходимо убедиться, что (i) его состояние не может измениться (например, если объект содержит final List, никаких мутирующих операций ( добавить, удалить...) необходимо выполнить после построения) и (ii) вы не разрешите this избежать во время строительства
  • Эффективно неизменяемый объект является безопасным потоком после его безопасного опубликования.
  • Пример небезопасной публикации:

    class EffectivelyImmutable {
        static EffectivelyImmutable unsafe;
        private int i;
        public EffectivelyImmutable (int i) { this.i = i; }
        public int get() { return i; }
    }
    
    // in some thread
    EffectivelyImmutable.unsafe = new EffectivelyImmutable(1);
    
    //in some other thread
    if (EffectivelyImmutable.unsafe != null
        && EffectivelyImmutable.unsafe.get() != 1)
        System.out.println("What???");
    

    Эта программа могла теоретически напечатать What???. Если i были окончательными, это не было бы юридическим результатом.

Ответ 2

Вы можете легко гарантировать неизменность только инкапсулированием, поэтому это необязательно:

// This is trivially immutable.
public class Foo {
    private String bar;
    public Foo(String bar) {
        this.bar = bar;
    }
    public String getBar() {
        return bar;
    }
}

Однако в некоторых случаях вы также должны гарантировать его инкапсуляцию, поэтому этого недостаточно:

public class Womble {
    private final List<String> cabbages;
    public Womble(List<String> cabbages) {
        this.cabbages = cabbages;
    }
    public List<String> getCabbages() {
        return cabbages;
    }
}
// ...
Womble w = new Womble(...);
// This might count as mutation in your design. (Or it might not.)
w.getCabbages().add("cabbage"); 

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

Ответ 3

Неизменяемые объекты НЕ ДОЛЖНЫ быть изменены каким-либо образом после их создания. Конечно, конечно это помогает. Вы гарантируете, что они никогда не будут изменены. НО, если у вас есть массив внутри вашего объекта, который является окончательным? Конечно, ссылка не меняющаяся, но элементы есть. Посмотрите здесь почти на тот же вопрос, который я дал также:

Ссылка

Ответ 4

Просто объявление объекта как final не делает его неотъемлемо неизменным. Возьмем, к примеру, этот класс:

import java.util.Date;

/**
* Planet is an immutable class, since there is no way to change
* its state after construction.
*/
public final class Planet {

  public Planet (double aMass, String aName, Date aDateOfDiscovery) {
     fMass = aMass;
     fName = aName;
     //make a private copy of aDateOfDiscovery
     //this is the only way to keep the fDateOfDiscovery
     //field private, and shields this class from any changes that 
     //the caller may make to the original aDateOfDiscovery object
     fDateOfDiscovery = new Date(aDateOfDiscovery.getTime());
  }

  /**
  * Returns a primitive value.
  *
  * The caller can do whatever they want with the return value, without 
  * affecting the internals of this class. Why? Because this is a primitive 
  * value. The caller sees its "own" double that simply has the
  * same value as fMass.
  */
  public double getMass() {
    return fMass;
  }

  /**
  * Returns an immutable object.
  *
  * The caller gets a direct reference to the internal field. But this is not 
  * dangerous, since String is immutable and cannot be changed.
  */
  public String getName() {
    return fName;
  }

//  /**
//  * Returns a mutable object - likely bad style.
//  *
//  * The caller gets a direct reference to the internal field. This is usually dangerous, 
//  * since the Date object state can be changed both by this class and its caller.
//  * That is, this class is no longer in complete control of fDate.
//  */
//  public Date getDateOfDiscovery() {
//    return fDateOfDiscovery;
//  }

  /**
  * Returns a mutable object - good style.
  * 
  * Returns a defensive copy of the field.
  * The caller of this method can do anything they want with the
  * returned Date object, without affecting the internals of this
  * class in any way. Why? Because they do not have a reference to 
  * fDate. Rather, they are playing with a second Date that initially has the 
  * same data as fDate.
  */
  public Date getDateOfDiscovery() {
    return new Date(fDateOfDiscovery.getTime());
  }

  // PRIVATE //

  /**
  * Final primitive data is always immutable.
  */
  private final double fMass;

  /**
  * An immutable object field. (String objects never change state.)
  */
  private final String fName;

  /**
  * A mutable object field. In this case, the state of this mutable field
  * is to be changed only by this class. (In other cases, it makes perfect
  * sense to allow the state of a field to be changed outside the native
  * class; this is the case when a field acts as a "pointer" to an object
  * created elsewhere.)
  */
  private final Date fDateOfDiscovery;
}

Ответ 5

Неизменяемость = не изменчива. Так что создание окончательных свойств - хорошая идея. Если не все свойства объекта защищены от изменения, я бы не сказал, что объект является неизменным.

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

Ответ 6

Нет.

Например, см. реализацию java.lang.String. Строки неизменны в Java, но поле hash не является окончательным (оно лениво вычисляется при первом вызове hashCode и затем кэшируется). Но это работает, потому что hash может принимать только одно значение nondefault, которое будет одинаковым при каждом его вычислении.

Ответ 7

Класс String неизменен, но свойство hash не является окончательным

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

В строковом классе hashcode фактически вычисляется на конечном массиве символов, который не изменится, если String построил. Поэтому неизменяемый класс может содержать изменчивые поля/свойства, но он должен удостовериться, что доступ к полю/свойству будет давать одинаковый результат при каждом обращении.

Чтобы ответить на ваш вопрос, необязательно, чтобы все поля были окончательными в неизменяемом классе.

Для дальнейшего чтения посетите здесь [блог]: http://javaunturnedtopics.blogspot.in/2016/07/string-is-immutable-and-property-hash.html

Ответ 8

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

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