Я запускаю с Java, и я узнаю о методах set, методах считывания и инкапсуляции. У меня есть очень простая программа, два класса:
Container
имеет частный международный массив (numArray
) с его методом set и методом считывания.
Main
создает a Container
объект и использование это в totalArray
метод.
public class Container {
private int numArray[]= {0,0,0};
public int[] getNumArray() {
return numArray;
}
public void setNumArray(int index, int value){
numArray[index] = value;
}
}
public class Main {
public static void main(String[] args) {
Container conte = new Container();
System.out.println(totalArray(conte.getNumArray()));
conte.getNumArray()[2]++;
System.out.println(totalArray(conte.getNumArray()));
}
private static int totalArray (int v[]){
int total=0;
for (int conta =0; conta<v.length;conta++){
total+=v[conta];
}
return total;
}
}
Проблема: Я могу изменить частный международный массив через метод считывания, я знаю поэтому getNumArray
возвращает ссылку на numArray
, не сам массив. Если бы я интересовался единственным элементом массива, то я сделал бы метод считывания с индексным значением, но я хочу целый массив для totalArray
метод.
Как я могу предотвратить numArray
от того, чтобы быть измененным из его класса?
Все, что вы можете сделать, чтобы предотвратить изменение массива, это предоставить его копию в геттере.
public int[] getArray() {
return Arrays.copyOf(numArray, numArray.length);
}
Таким образом, другие методы могут изменить свою собственную копию массива, но когда они снова вызывают геттер, они получают оригинальную версию, без изменений. Только setNumArray()
, который вы предоставляете, может на самом деле изменить ваш внутренний массив.
Иначе, если вы хотите полностью заблокировать контейнер, вы должны бросить массивы и использовать неизменяемый объект. Некоторые библиотеки предоставляют неизменяемые списки или используют Collections.unmodifiableList.
.Обычно вы смотрите на интерфейс, который вы пытаетесь предоставить вызывающим абонентам вашего класса.
Методы типа:
void addItem(Object item);
Object getItem(int index);
int getSize();
- это те вещи, которые вы бы предоставили в вашем контейнерном классе. Затем они взаимодействуют с приватным массивом от имени вызывающего абонента.
Если вы хотите вернуть весь массив, не допуская изменений, вы можете в геттере скопировать массив в новый и вернуть копию.
В качестве альтернативы, если вы используете классы коллекции Java вместо примитивного массива, они предоставляют метод unmodifiableXXX() (например, Collections.unmodifiableList(myList)
), который предоставляет обертку вокруг коллекции только для чтения.
Если вы хотите вернуть массив, вы должны клонировать его:
public int[] getArray() {
return (int[]) numArray.clone();
}
В публичном API вы должны быть уверены, что вы четко документируете это вызывающим абонентам (на самом деле, в любом случае, если они получают массив, который изменит состояние класса или нет - они должны знать)
.Инкапсулирование - это процесс сокрытия реализации. Независимо от того, хранится коллекция данных в массиве или нет, это деталь реализации; если она инкапсулирована, вы захотите иметь возможность изменить ее на другой тип хранения.
Сам факт, что вы подвергаете состояние (или производное состояние) как геттеры и сеттеры, нарушает инкапсуляцию и подразумевает, что вы реализуете абстрактный тип данных, а не истинный объектно-ориентированный класс. Например, ArrayList
- это тип данных, который не представляет собой никакой истинной инкапсуляции поведения в приложении. Будет ли это то, что вы хотите, зависит от того, как и где этот тип будет использоваться.
Я бы попытался либо заставить Container
реализовать Iterable
для внешнего взаимодействия, если это просто контейнерный тип данных, либо предоставить внутренний итерационный метод, которому вы передаете посетителя, если он предназначен в качестве инкапсулируемого класса. Если это абстрактный тип данных, то настоятельно рекомендуется использовать вместо него встроенные, такие как int[]
или List
.