Ответ 1
Вы должны вернуть копию вашего массива.
public String[] getArr() {
return arr == null ? null : Arrays.copyOf(arr, arr.length);
}
Представьте, что у меня есть этот класс:
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. Мне просто нужно знать, как правильно это сделать).
Вы должны вернуть копию вашего массива.
public String[] getArr() {
return arr == null ? null : Arrays.copyOf(arr, arr.length);
}
Если вы можете использовать Список вместо массива, Коллекции предоставляют неизменяемый список:
public List<String> getList() {
return Collections.unmodifiableList(list);
}
Модификатор 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];
}
Collections.unmodifiableList
уже упоминалось - Arrays.asList()
странно нет! Моим решением было бы также использовать список извне и обернуть массив следующим образом:
String[] arr = new String[]{"1", "2"};
public List<String> getList() {
return Collections.unmodifiableList(Arrays.asList(arr));
}
Проблема с копированием массива заключается в следующем: если вы делаете это каждый раз, когда получаете доступ к коду, и массив большой, вы наверняка создадите много работы для сборщика мусора. Итак, копия - простой, но очень плохой подход - я бы сказал, "дешево", но дорого стоит! Особенно, если у вас больше двух элементов.
Если вы посмотрите на исходный код Arrays.asList
и Collections.unmodifiableList
, на самом деле не так много создано. Первый просто обертывает массив, не копируя его, второй просто обертывает список, делая изменения в нем недоступными.
Вы также можете использовать 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);
}
}
с этой точки зрения вы должны использовать копию системного массива:
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;
}
}
Вы можете вернуть копию данных. Вызывающий, который хочет изменить данные, будет изменять только копию
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;
}
}
Проблема заключается в том, что вы возвращаете указатель на изменяемый объект. К сожалению. Либо вы визуализируете объект неизменяемым (немодифицируемое решение списка), либо возвращаете копию объекта.
Как правило, конечная цель объектов не защищает объекты от изменения, если они изменяемы. Эти две проблемы - "целовать двоюродных братьев".
Возвращение немодифицируемого списка - хорошая идея. Но список, который немодифицируется во время вызова метода геттера, по-прежнему может быть изменен классом или классами, производными от класса.
Вместо этого вы должны дать понять кому-либо, который расширяет класс, который не должен быть изменен.
Итак, в вашем примере это может привести к следующему коду:
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;
}
}
Да, вы должны вернуть копию массива:
public String[] getArr()
{
return Arrays.copyOf(arr);
}