Как сохранить параметр объекта неизменным в выполняемом классе в Java?
У меня есть управляемый класс, такой как:
Class R1 implements Runnable {
private static final Log LOGGER = LogFactory.getLog(R1.class);
private final ObjectClass obj;
private final SomeService service;
public R1(ObjectClass obj, SomeService service) {
this.obj = obj;
this.service = service;
}
@override
public void run() {
String value = this.obj.getSomeValue();
LOGGER.debug("Value is " + value);
// some actions, such as:
// service.someMethod(obj);
}
}
Я использую объект ExecutorService для выполнения R1 и помещаю R1 в очередь. Но позже вне R1 я изменяю значение в ObjectClass, который я передал в R1, поэтому действия в R1 после getSomeValue () ведут себя не так, как я ожидал. Если я хочу сохранить значение ObjectClass object в R1 неизменным, что я могу сделать? Предположим, объект большой и имеет множество методов get и set.
Чтобы сделать задачу более ясной, мне нужно передать obj в a объект класса обслуживания, который также используется в качестве параметра в классе runnable. Я изменил исходные коды соответствующим образом.
7 ответов:
Согласно комментариям, очевидно, Мое предлагаемое решение имеет проблемы.
Таким образом, следуйте другим предложениям о создании нового экземпляра и копировании требуемых свойств. Или создайте легкий объект данных, содержащий требуемые свойства. В любом случае, я считаю, что вам нужно 2 экземпляра, чтобы сделать то, что вы хотите.
Я предлагаю вам реализовать метод clone, который создает новый пример.
Http://download.oracle.com/javase/1,5,0/docs/api/java/lang/Cloneable.html
Проблема здесь в том, что вы передали экземпляр в свой R1class, но это все тот же единственный экземпляр, поэтому изменения в нем повлияют на все остальное. Таким образом, реализация метода клонирования позволит вам легко создать копию вашего экземпляра, которая может быть использована в вашем классе R1, позволяя вам вносить дальнейшие изменения в ваш оригинал.
В вашем классе R1,
public R1(ObjectClass obj) { //this.obj = obj; this.obj = obj.clone(); }
P.S. Вы должны реализовать этот метод самостоятельно. Это не просто автоматически даст вам глубокую копию.
В зависимости от характера вашей программы, есть несколько вариантов.
Вы можете "разумно переопределить клонирование" (пункт 11 вэффективной Java ) и клонировать объект, прежде чем передать его исполняемому. Если переопределение clone не работает для вас, возможно, лучше сделать одно из следующих действий:
- создайте новый экземпляр объекта вручную и скопируйте значения из
obj
.- добавьтеподмножество данных, содержащихся в
obj
. Так что вместо передаваяobj
в конструктор, вы бы передали вsomeValue
. Я бы поддержал этот метод, чтобы Вы только снабжалиR1
необходимыми данными, а не всем объектом.Альтернативно, если не имеет значения, что данные в
obj
изменяются до выполненияR1
, то вам только Нужно убедиться, чтоobj
не изменяется , ПокаR1
исполняет. В этом случае вы можете добавить ключевое словоsynchronize
в методgetSomeValue()
., и тогда придетсяR1
синхронизировать наobj
Вот так:@Override public void run() { synchronize (obj) { String value = obj.getSomeValue(); } // some actions. }
Если объект слишком большой,
Может быть, лучше использовать неизменяемый ParameterObject с достаточным количеством данных / методов.
Если возможно, попробуйте сделать свой ObjectClass неизменяемым. (изменения состояния не поддерживаются). В Java вы должны "сделать это сами"; нет понятия объекта const (как в C++)
Возможно, вы можете иметь свой orig ObjectClass, но создать новый класс ImmutableObjectClass, который принимает ваш orig в ctor.
Предположение: вас не волнует, если
R1
работает со старыми данными.Затем вы можете изменить свой код на:
public class R1 implements Runnable { private final String value; // Option 1: Pull out the String in the constructor. public R1(ObjectClass obj) { this.value = obj.getSomeValue(); // Now it is immutable } // Option 2: Pass the String directly into the constructor. public R1(String value) { this.value = value; // This constructor has no coupling } @Override public void run() { // Do stuff with value } }
Если вы хотите, чтобы
R1
работали с последними данными, в отличие от того, какими они были, когда вы их создавали, то вам потребуется некоторый тип синхронизации междуR1
и модификацией данных.
"Проблема" здесь заключается в том, что в соответствии с моделью памяти Java потоки могут (и делают) кэшировать значения полей. Это означает, что если один поток обновляет поле (объекта ObjectClass), другие потоки не будут "видеть" изменения - они все равно будут смотреть на свое кэшированное (устаревшее) значение.
Чтобы сделать изменения видимыми в потоках, у вас есть два варианта:
- сделайте поля, которые вы будете изменять в ObjectClass
volatile
- ключевое слово volatile заставляет потоки не кэшировать значение поля (т. е. всегда использовать последнее значение)- синхронизировать доступ, как чтение, так и запись, к полям-все изменения, сделанные в синхронизированном блоке, видны другим потокам, синхронизирующимся с тем же объектом блокировки (если вы синхронизируете методы, объект
this
используется в качестве блокировки)