2013-11-09 2 views
1

Я работаю над обновлением базового уровня Java-кода, чтобы он следовал хорошей практике безопасности и столкнулся с проблемой, связанной с дженериками. Скажем, у вас есть следующие:Предотвращение изменчивости для общих типов Java

public class SomeClass<T> 
{ 
    private T value; 
    public T getValue() 
    { 
     return value; 
    } 

    public void setValue(T value) 
    { 
     this.value = value; 
    } 
} 

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

+0

Я думаю, что вы проиграли здесь из-за того, как структурирована Java. Интерфейс Cloneable является причудливой старой шуткой, поэтому вы не можете положиться на это, и в Java нет ничего, что позволило бы объявить общий объект только для чтения. С положительной стороны клиентский код выбирает, какие классы содержатся в SomeClass, поэтому клиентский код может выбрать создание неизменяемых версий объектов, прежде чем хранить их в SomeClass. Совершенно очевидно, но я не думаю, что вы можете гарантировать, что вы хотите получить воздухонепроницаемый результат. – Bobulous

+0

Да, к сожалению (хотя действительно ли неудачно для обсуждения), C++ делает это отлично, но Java никогда не собирался это делать. –

ответ

0

Я вижу три подхода:

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

  2. Поддерживайте неизменяемые типы.

  3. Удалить getValue(). Вместо этого предоставьте методы, которые работают непосредственно на this.value, не выставляя его вне класса. При таком подходе setValue() все еще может быть проблематичным (вам нужно убедиться, что вызывающий объект не держится за ссылку на объект после вызова setValue()).

Если T может быть произвольный тип, который вы не имеете никакого контроля над, то варианты 1 и 2 не будут пригодны.

+0

Я ценю обратную связь. Я думаю, что правильный подход к коду, который я меняю, - это вариант 2. У меня есть контроль над типами, поэтому для типов, которые я хочу поддерживать, которые не являются примитивными объектами-оболочками, неизменяемыми, это не займет много времени. –

1

Как я понимаю, вы хотите, чтобы ничто снаружи SomeClass не могло мутировать объект value.

В C++ вы можете вернуть ссылку на const (вообще не копировать), но Java этого не имеет. Итак, давайте посмотрим на копирование ...

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

Но как насчет объектов, которые являются copiable?

В Java вы не можете вызвать конструктор копирования (или любой другой конструктор) общего типа (Calling constructor of a generic type).

Интерфейс Cloneable, но это действительно не более, чем обещание, что clone работает; фактически не выставлен clone Публично. Таким образом, для дженериков вы должны использовать отражение, как показано на рисунке here.

К сожалению, нет правильного решения. Единственный жизнеспособный (за исключением изменения цели или семантики вашего класса) заключается в использовании метода clone, как показано в приведенной выше ссылке, и понимать, что некоторые объекты не могут быть скопированы.

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

0

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

public class Stack { 


    public static void main(String[] args) { 
     SomeClass<Animal> sc = new SomeClass<>(); //This generate an error because doesnt implements Clonable interface 
     SomeClass<Person> sc1 = new SomeClass<>(); 
    } 

} 
class SomeClass<T extends Comparable> //Note that extends means implements or the common extends 
{ 
    private T value; 
    public T getValue() 
    { 
     return value; 
    } 

    public void setValue(T value) 
    { 
     this.value = value; 
    } 
} 

class Person implements Comparable<Person>{ 
    @Override 
    public int compareTo(Person p){ 
     return 0; 
    } 
} 
class Animal { 

} 

Хотел бы я вам помочь. :)

0

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

Foo foo = myEntity1.getFoo(); 
foo.bar = 23; 
myEntity2.setFoo(foo); 
foo.bar = 47; 
myEntity3.setFoo(foo); 

нет четкого указания того, или как изменение foo.bar будет влиять на различные объекты. Если код вместо этого был:

Foo foo = myEntity1.getFoo(); 
foo = foo.withBar(23); // makes a new instance which is like foo, but where bar==23 
myEntity2.setFoo(foo); 
foo = foo.withBar(47); // makes a new instance which is like foo, but where bar==47 
myEntity3.setFoo(foo); 

было бы очень ясно, что bar свойство myEntity1 «s foo не будут затронуты, что из myEntity2 будет 23, и что из myEntity3 будет 47. Если foo является изменяемый класс, шаблон должен быть:

Foo foo = new Foo(); 
myEntity1.writeTo(foo); // Copy properties from myEntity1 to the supplied instance 
foo.bar = 23; 
myEntity2.readFrom(foo); // Copy properties from the supplied instance to myEntity2 
foo.bar = 47; 
myEntity2.readFrom(foo); // Copy properties from the supplied instance to myEntity3 

Здесь myEntity1 не давая абоненту объекта, но вместо того, чтобы копировать данные в объект, предоставленный абонентом. Следовательно, гораздо яснее, что вызывающий абонент не должен ожидать, что записи foo.bar будут влиять на объекты напрямую, а просто изменить то, что будет записано в последующих вызовах readFrom.

Смежные вопросы