Как мне объявить постоянный набор видимым для каждого экземпляра класса? - PullRequest
3 голосов
/ 10 марта 2010

Я хотел бы, чтобы в моем классе была установлена ​​константа, видимая для всех экземпляров класса.

Во-первых, я не знаю, нужно ли мне объявить его как "статический". Насколько я понимаю, любые изменения статического поля (сделанные одним из экземпляров) будут видны другим экземплярам (поэтому статическая переменная не привязана к конкретному экземпляру). Более того, статическое поле может быть изменено без использования какого-либо экземпляра (мы работаем напрямую с классом). Итак, все эти особые свойства статического поля связаны с тем, как его можно изменить и каков будет эффект этих изменений. Но в моем случае я бы хотел иметь постоянную (поэтому вопросы «изменений» здесь не актуальны). Так что, вероятно, мне не нужно использовать «статический». Правильно?

Во-вторых, мой набор будет содержать много элементов, и я не хочу определять значение набора сразу (когда я создаю переменную). Другими словами, я хотел бы объявить набор, затем шаг за шагом добавлять элементы в этот набор. Но я не могу сделать это, если я работаю с константами. Можно ли указать указанное значение набора, а затем сделать его постоянным?

В-третьих, я понял, что могут возникнуть некоторые проблемы, если я попытаюсь изменить значение переменных вне какого-либо метода. Итак, как это работает?

ДОБАВЛЕНО:

OK. Благодаря ответу я понял, что это должны быть «final» и «static» (поскольку это постоянный набор и он не будет связан с каким-либо конкретным экземпляром, он просто должен быть видимым для всех экземпляров класса). Однако у меня все еще есть проблема. Я хотел указать набор с помощью «добавить», и я не могу добавить к набору, если он постоянный (окончательный). Более того, я не могу изменить значения переменных вне методов (почему?). Во всяком случае, я не настаиваю на использовании «добавить» для определения набора. Я готов определить это сразу. Но я не знаю, как это сделать. Я пробовал такие вещи:

final static Set allowedParameters = new HashSet("aaa","bbb");
final static Set allowedParameters = new HashSet(["aaa","bbb"]);
final static Set allowedParameters = new HashSet({"aaa","bbb"});
final static Set allowedParameters = new HashSet(Arrays.asList({"userName"}));

И они не работали.

ДОБАВЛЕНО 2:

Кто-нибудь может мне объяснить, пожалуйста, код, данный Тадеушем Копеком?

class YourClass {
    private static void fillSet(Set<SomeType> set) {
        // here you add elements, like
        set.add(new SomeType());
    }
    private final static Set<SomeType> yourSetField;
    static {
        final Set<SomeType> tempSet = new HashSet<SomeType>();
        fillSet(tempSet);
        yourSetField = Collection.unmodifiableSet(tempSet);
    }
}


1. У метода fillSet есть одна переменная с именем "set". Почему это не используется в методе?
2. Что такое SomeType() в fillSet? Что делает этот метод?
3. Что делает fillSet? Далее в примере мы даем пустой набор этому методу. Зачем?
4. Зачем мы объявляем tempSet окончательным?
5. Что именно unmodifiableSet делает? Я думаю, что по названию он генерирует набор, который нельзя изменить. Но почему было бы недостаточно объявить yourSetField как final? Чем бы он тоже был постоянным.

Ответы [ 4 ]

6 голосов
/ 10 марта 2010

Хотите добавить элементы в свой набор один раз, а затем только читать его содержимое или хотите добавить элементы в него в любое время? Если вы создадите его один раз, сделайте это так:

class YourClass {
    private static void fillSet(Set<SomeType> set) {
        // here you add elements, like
        set.add(new SomeType());
    }
    private final static Set<SomeType> yourSetField;
    static {
        final Set<SomeType> tempSet = new HashSet<SomeType>();
        fillSet(tempSet);
        yourSetField = Collections.unmodifiableSet(tempSet);
    }
}

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

Если вы хотите добавить элементы в любое время, у вас есть проблема с параллелизмом - прочитайте ответ extraneon.

EDIT
В ответ на запрос я объясню, что делает этот код.

Сначала загадочные <> скобки: Я предположил, что вы используете Java 1.5 или выше и использовали generics . В двух словах - если вы объявляете переменную типа List, она содержит объекты. Если вы хотите сохранить в нем строки, вы должны их разыграть, когда извлекаете их из своего списка. Пример

List myList = new ArrayList();
myList.add("Hello, my Jon Skeet Number decreases");
String firstElement = (String) myList.get(0);

Требуется приведение к String. Более того, ничто не мешает вам добавить BigDecimal в myList. Но если вы получите его и попытаетесь привести к типу String, вы получите ClassCastException.

myList.add(0, BigDecimal.ZERO); // perfectly legal
String anotherString = (String) myList.get(0); // compiles, but ClassCastException at runtime

Итак, в Java 1.5 введено обобщений . Вы можете указать, что список может содержать только строки. Синтаксис использует <> скобки:

List<String> myList = new ArrayList<String>();
myList.add("Hi everybody");
String firstElem = myList.get(0); // no cast required
myList.add(BigDecimal.ZERO); // compiler error: cannot cast BigDecimal to String

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

Теперь - статические блоки. Существует два способа инициализации статических полей - непосредственно в их объявлении:

static private int instanceCount = 0;

Это полезно, если начальное значение является простым выражением.
Или в статический блок инициализации

static {
    // some code, that can use class static variables, class static methods, declare its own variables etc.
}

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

А теперь ваши вопросы

  1. Используется параметр set из fillSet. К этому добавлен элемент: set.add(new SomeType());
  2. Поскольку я не знал, что вы хотите оставить в своем наборе, я назвал тип его элементов SomeType. Он должен быть заменен типом, который вы хотите использовать. new SomeType(); создает экземпляр (гипотетического) SomeType, вызывающего его конструктор без параметров.
  3. fillSet делает именно то, что означает его имя - берет набор и заполняет его (добавляет к нему некоторые значения). Мы даем ему пустой набор, чтобы в результате мы получили набор с элементами fillSet. fillSet - это место, куда вы должны поместить весь код, который инициализирует ваш набор. Хорошо, что это отделено.
  4. tempSet - это локальная переменная в блоке статической инициализации, которая назначается один раз и никогда не переназначается. Чтобы выразить это, я объявляю это окончательным. Это привычка, которую я получил от использования findBugs , и я думаю, что это хорошо для удобства чтения.
  5. Завершение yourSetField означает, что вы не можете написать yourSetField = new HashSet<SomeType>() после его инициализации. Но любой, кто может получить доступ к yourSetField, может написать yourSetField.add(...). Если yourSetField является неизменяемым набором, добавление к нему вызовет исключение во время выполнения (UnsupportedOperationException). Другими словами: final означает, что вы не можете заставить yourSetField указывать на другой объект (и компилятор гарантирует это). unmodifiableSet означает, что вы не можете добавлять или удалять объекты из набора. Он будет скомпилирован, но сломается во время выполнения.
4 голосов
/ 10 марта 2010

В вашем классе вы хотите что-то вроде:

private static final Set<Foo> mySet;
static {
 // ...initialize contents here.  guava example looks like:
mySet = ImmutableSet.of( adc, 123, etc );

}

Я бы пошел с Guava ImmutableSet, как предлагает Джон, так что вы бы использовали метод of( ... ) или интерфейс конструктора (если у вас есть какой-то поток данных - если вы жестко кодируете данные, просто использование ()), оба из которых довольно хорошо описаны в API. Другие варианты включают обертывание с помощью неизменяемого метода из коллекций.

4 голосов
/ 10 марта 2010

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

Я предлагаю, чтобы у вас была конечная статическая переменная, и в блоке статического инициализатора для вашего типа вы строите регулярный набор, а затем создаете неизменный набор (например, один , предоставленный в Guava ) из обычного. Назначьте эту неизменяемую ссылку на набор вашей статической конечной переменной. Работа выполнена.

2 голосов
/ 10 марта 2010

Я думаю, вы знаете, что означает static. Как вы упомянули, набор, но не его содержимое, является «константой», то есть ни один экземпляр не может поместить в него другой набор, вам бы неплохо было сделать его final.

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

Теперь возникает другая проблема; параллелизм. Что если несколько экземпляров вашего класса m одифицируют Set одновременно? Что должен делать Сет? Вы можете поймать это, обернув свой набор в Синхронизированный набор .

В целом, я думаю, что декларация должна выглядеть так:

private static final Set<YourElement> mySet = Collections.synchronizedSet(new HashSet());

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

С таким объявлением утверждение типа

void myFoo() {
  mySet = new HashSet(); // will fail as it's final
}

потерпит неудачу, как и положено, и будут работать параллельные обновления набора.

Если вам нужен Set с постоянными значениями, вы можете сделать:

private static final Set<YourElement> mySet;
static {
   Set<YourElement> tmpSet = new HashSet();
   tmpSet.add(...);
   mySet = Collections.unmodifiableSet(tmpSet);
}

Но я вижу, что кто-то еще был первым:)

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