Создание неизменного объекта без конечных полей? - PullRequest
8 голосов
/ 21 сентября 2011

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

Если возможно, пара примеров будет полезна.

Ответы [ 7 ]

6 голосов
/ 21 сентября 2011

Да, это так - просто убедитесь, что ваше состояние является частным, и ничто в вашем классе не изменяет его:

public final class Foo
{
    private int x;

    public Foo(int x)
    {
        this.x = x;
    }

    public int getX()
    {
        return x;
    }
}

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

Однако:

  • Присвоение неконечных полей не имеет тех же самых правил видимости памяти, что и конечные поля, поэтому1009 * возможно можно наблюдать, как объект "меняется" из другого потока.См. раздел 17.5 JLS для получения более подробной информации о гарантиях для окончательных полей.
  • Если вы не планируете изменять значение поля, я бы лично сделал окончательное значение документ это решение и чтобы избежать случайного добавления метода мутации позже
  • Я не могу вспомнить, не мешает ли JVM мутировать конечные поля посредством отражения;очевидно, что любой вызывающий абонент с достаточными привилегиями может сделать поле x доступным в приведенном выше коде и изменить его с помощью отражения.(Согласно комментариям, можно сделать с окончательными полями, но результаты могут быть непредсказуемыми.)
6 голосов
/ 21 сентября 2011

Объявите все поля закрытыми и определите только получатели:

public final class Private{
    private int a;
    private int b;

    public int getA(){return this.a;}
    public int getB(){return this.b;}
}

, ссылаясь на комментарий @Jon Skeet, модификатор final будет полезен для:

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

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

3 голосов
/ 22 сентября 2011

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

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

Проблема состоит в том, чтобы дать официальную ссылку на это строгое использование термина «неизменный». Я не могу; это основано на том, как Java bigshots использует термин. Всякий раз, когда они говорят «неизменный объект», они всегда говорят о поточно-безопасных неизменяемых объектах.

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

1 голос
/ 21 сентября 2011

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

public final class Example {
    private int value;

    public Example(int value) {
        this.value = value;
    }

    public int getValue() {
        return value;
    }
}

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

1 голос
/ 21 сентября 2011

Я верю, что ответ - да.

рассмотрим следующий объект:

public class point{
   private int x; 
   private int y;
   public point(int x, int y)
   {
      this.x =x; 
      this.y =y;
    }

   public int getX()
    {
       return x;
    }

    public int getY()
    { 
        return y;
    }

}

Этот объект является неизменным.

1 голос
/ 21 сентября 2011

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

0 голосов
/ 21 сентября 2011

Да.Сделайте поля приватными.Не изменяйте их никакими методами, кроме конструктора.Конечно, в таком случае, почему бы вам не отметить их как окончательные ???

...