Как я могу использовать сеттер вместо конструктора для конечной переменной? - PullRequest
3 голосов
/ 13 июля 2010

Я анализирую XML-файл, в котором после создания объекта должно быть установлено одно из полей, которое я хочу сделать неизменным, ID. Должен ли я установить его в null и выдать исключение в методе setID (), если ID! = Null?

Edit: Я анализирую XML-файл и вначале создаю объект, поля и объекты которого заполняются с использованием информации в XML-файле. Я хочу иметь возможность установить ID, который должен быть неизменным, после создания корневого объекта.

Редактировать: изменил «окончательный» на «неизменный», потому что это действительно то, что я имел в виду семантически. (Извините :()

Ответы [ 8 ]

14 голосов
/ 13 июля 2010

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

9 голосов
/ 13 июля 2010

Вы не можете изменить конечный элемент за пределами конструктора.Вы должны сделать это не окончательным.

7 голосов
/ 13 июля 2010

Лучшим подходом может быть использование Builder, как описано в Effective Java 2nd Edition в пункте 2.

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

Конечный результат выглядит примерно так:

public class Foo {
  public static class Builder {
    public Foo build() {
      return new Foo(this);
    }

    public Builder setId(int id) {
      this.id = id;
      return this;
    }

    // you can set defaults for these here
    private int id;
  }

  public static Builder builder() {
      return new Builder();
  }

  private Foo(Builder builder) {
    id = builder.id;
  }

  private final int id;

  // The rest of Foo goes here...
}

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

Foo foo = Foo.builder()
    .setId(id)
    .build();

Вы также можете разделить это, конечно:

// I don't know the ID yet, but I want a place to store it.
Foo.Builder fooBuilder = Foo.builder();
...
// Now I know the ID:.
fooBuilder.setId(id);
...
// Now I'm done and want an immutable Foo.
Foo foo = fooBuilder.build();

У вас может быть несколько сеттеров в компоновщике и даже добавлены дополнительные параметры для build () или конструктора Builder.

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

6 голосов
/ 13 июля 2010

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

1 голос
/ 13 июля 2010

Вы определенно не можете сделать это final.Выдача исключения из setID(), если ID != null - хорошая идея.Может быть, если вы предоставите более подробную информацию, кто-нибудь может предложить более креативное решение?

0 голосов
/ 14 июля 2010

Технически, если вы можете изменить значение после выполнения конструктора, оно не является «неизменяемым».Это более "полуизменяемый".

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

Идея объекта-строителя тоже хороша.Я никогда не использовал этот шаблон.«Надо продумать все за и против.

0 голосов
/ 13 июля 2010

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

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

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

Если вы не предоставите установщик для идентификатора, пользователи могут только «установить» идентификатор при создании объекта, и я думаю, это то, что вы хотите.

0 голосов
/ 13 июля 2010

Поле не может быть объявлено final, если оно не инициализировано во время построения.Я думаю, что-то подобное может соответствовать вашим требованиям.

class Foo {

  private Bar x;

  void setX(Bar x) {
    if (x == null)
      throw new IllegalArgumentException();
    if (this.x != null)
      throw new IllegalStateException();
    this.x = x;
  }

}

Эта реализация Foo предназначена для доступа одним потоком.Для многопоточного доступа вы можете использовать AtomicReference или внешний synchronize доступ.

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

...