Как предотвратить изменение частного поля в классе?

Представьте, что у меня есть этот класс:

public class Test
{
  private String[] arr = new String[]{"1","2"};    

  public String[] getArr() 
  {
    return arr;
  }
}

Теперь у меня есть другой класс, который использует вышеуказанный класс:

Test test = new Test();
test.getArr()[0] ="some value!"; //!!!

Итак, это проблема: я получил доступ к закрытому полю класса извне! Как я могу это предотвратить? Я имею в виду, как я могу сделать этот массив неизменным? Означает ли это, что с каждым методом getter вы можете работать, чтобы получить доступ к частному полю? (Я не хочу никаких библиотек, таких как Guava. Мне просто нужно знать, как правильно это сделать).

Ответ 1

Вы должны вернуть копию вашего массива.

public String[] getArr() {
  return arr == null ? null : Arrays.copyOf(arr, arr.length);
}

Ответ 3

Модификатор private защищает только само поле от доступа от других классов, но не ссылки на объекты в этом поле. Если вам нужно защитить ссылочный объект, просто не выдавайте его. Изменение

public String [] getArr ()
{
    return arr;
}

в

public String [] getArr ()
{
    return arr.clone ();
}

или

public int getArrLength ()
{
    return arr.length;
}

public String getArrElementAt (int index)
{
    return arr [index];
}

Ответ 4

Collections.unmodifiableList уже упоминалось - Arrays.asList() странно нет! Моим решением было бы также использовать список извне и обернуть массив следующим образом:

String[] arr = new String[]{"1", "2"}; 
public List<String> getList() {
    return Collections.unmodifiableList(Arrays.asList(arr));
}

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

Если вы посмотрите на исходный код Arrays.asList и Collections.unmodifiableList, на самом деле не так много создано. Первый просто обертывает массив, не копируя его, второй просто обертывает список, делая изменения в нем недоступными.

Ответ 5

Вы также можете использовать ImmutableList, который должен быть лучше стандартного unmodifiableList. Класс является частью Guava библиотек, которые были созданы Google.

Вот описание:

В отличие от Collections.unmodifiableList(java.util.List), который представляет собой просмотр отдельной коллекции, которую все еще можно изменить, экземпляр ImmutableList содержит свои личные данные и никогда не изменяет

Вот простой пример того, как его использовать:

public class Test
{
  private String[] arr = new String[]{"1","2"};    

  public ImmutableList<String> getArr() 
  {
    return ImmutableList.copyOf(arr);
  }
}

Ответ 6

с этой точки зрения вы должны использовать копию системного массива:

public String[] getArr() {
   if (arr != null) {
      String[] arrcpy = new String[arr.length];
      System.arraycopy(arr, 0, arrcpy, 0, arr.length);
      return arrcpy;
   } else
      return null;
   }
}

Ответ 7

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

public class Test {
    private static String[] arr = new String[] { "1", "2" };

    public String[] getArr() {

        String[] b = new String[arr.length];

        System.arraycopy(arr, 0, b, 0, arr.length);

        return b;
    }
}

Ответ 8

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

Как правило, конечная цель объектов не защищает объекты от изменения, если они изменяемы. Эти две проблемы - "целовать двоюродных братьев".

Ответ 9

Возвращение немодифицируемого списка - хорошая идея. Но список, который немодифицируется во время вызова метода геттера, по-прежнему может быть изменен классом или классами, производными от класса.

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

Итак, в вашем примере это может привести к следующему коду:

import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class Test {
    public static final List<String> STRINGS =
        Collections.unmodifiableList(
            Arrays.asList("1", "2"));

    public final List<String> getStrings() {
        return STRINGS;
    }
}

В приведенном выше примере я сделал поле STRINGS общедоступным, в принципе вы можете покончить с вызовом метода, поскольку значения уже известны.

Вы также можете назначить строки в поле private final List<String>, которое немодифицируется во время построения экземпляра класса. Использование аргументов константы или экземпляра (конструктора) зависит от дизайна класса.

import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class Test {
    private final List<String> strings;

    public Test(final String ... strings) {
        this.strings = Collections.unmodifiableList(Arrays
                .asList(strings));
    }

    public final List<String> getStrings() {
        return strings;
    }
}

Ответ 10

Да, вы должны вернуть копию массива:

 public String[] getArr()
 {
    return Arrays.copyOf(arr);
 }