Вопрос

Я начинаю с Java и изучаю сеттеры, геттеры и инкапсуляцию.У меня очень простая программа, два класса:

  • Container имеет частный массив int (numArray) с его сеттером и геттером.

  • Main создает 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;
    }
}

Проблема:Я могу изменить частный массив int через геттер, я знаю, потому что getNumArray возвращает ссылку на numArray, а не сам массив.Если бы меня интересовал один элемент массива, я бы создал метод получения со значением индекса, но мне нужен весь массив для totalArray метод.

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

Это было полезно?

Решение

Все, что вы можете сделать, чтобы люди не могли изменить ваш массив, — это предоставить его копию в геттере.

public int[] getArray() {
    return Arrays.copyOf(numArray, numArray.length);
}

Таким образом, другие методы могут изменить свою копию массива, но когда они снова вызовут геттер, они получат исходную версию без изменений.Только setNumArray() вы можете фактически изменить ваш внутренний массив.

В противном случае, если вы хотите полностью заблокировать контейнер, вам придется удалить массивы и использовать неизменяемый объект. Некоторый библиотеки предоставляют неизменяемые списки или используют Коллекции.unmodifyingList.

Другие советы

Если вы хотите вернуть массив, вы должны клонировать его:

  public int[] getArray() {
       return (int[]) numArray.clone();
  }

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

Обычно вы смотрите на интерфейс, который пытаетесь предоставить вызывающим абонентам вашего класса.

Такие методы, как:

void addItem(Object item);
Object getItem(int index);
int getSize();

— это те вещи, которые вы бы предоставили в своем классе контейнера.Затем они взаимодействуют с частным массивом от имени вызывающего абонента.

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

В качестве альтернативы, если вы используете классы коллекций Java вместо примитивного массива, они предоставляют неизменяемый методXXX() (например, Collections.unmodifiableList(myList)), которые предоставляют оболочку коллекции, доступную только для чтения.

Инкапсуляция — это процесс сокрытия реализации.Сохраняет ли коллекция свои данные в массиве или нет, это вопрос реализации;если бы он был инкапсулирован, вы бы хотели иметь возможность изменить его на другой тип хранилища.

Сам факт того, что вы представляете состояние (или производное состояние) как геттеры и сеттеры, нарушает инкапсуляцию и подразумевает, что вы реализуете абстрактный тип данных, а не настоящий объектно-ориентированный класс.Например, ArrayList — это тип данных, который не представляет никакой истинной инкапсуляции поведения приложения.То, хотите ли вы этого, зависит от того, как и где этот тип будет использоваться.

Я бы склонился к тому, чтобы либо сделать Container осуществлять Iterable<Integer> для внешнего взаимодействия, если это просто тип данных контейнера, или предоставьте внутренний метод итератора, которому вы передаете посетителя, если он задуман как инкапсулированный класс.Если это абстрактный тип данных, настоятельно рекомендуется использовать встроенные, такие как int[] или List<Integer> вместо.

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

private A[] items;

public List<A> getItems() {
    return Collections.unmodifiableList(Arrays.asList(items));
}

Как инкапсулировать массив в Java

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

Если вы настаиваете, верните клонировать принадлежащий множество / защитная копия это путь для простых случаев.

Я хотел бы предложить другой подход из всех ответов, которые я видел здесь.Когда вы думаете об инкапсуляции, полезно также подумать о правиле: «Не запрашивайте у объекта его данные, попросите объект работать со своими данными за вас».

Ты не дал мне применения numArray, я собираюсь представить, что вашей целью было создать числовой «вектор» из математических вычислений (а не вектор Java) для примера.

Итак, вы создаете класс NumericVector, который содержит массив двойных значений.Ваш NumericVector будет иметь такие методы, как multiplyByScalar(double scalar) и addVector(NumericVector SecondVector) для добавления элементов.

Ваш внутренний массив полностью инкапсулирован — он никогда не ускользнет.Любая операция, выполняемая с ним, выполняется в вашем классе NumericVector с помощью этих «бизнес-методов».Как вы отображаете его после работы с ним?Иметь переопределение NumericVector NumericVector.toString() чтобы он печатался правильно, или, если у вас есть графический интерфейс, напишите класс «Контроллер» для передачи данных из вашей модели (NumbericVector) в ваше представление (GUI).Для этого может потребоваться способ потоковой передачи элементов из вашего NumericVector.

Это также указывает на несколько вещей, которых следует избегать:Не создавайте автоматически сеттеры и геттеры, они нарушают вашу инкапсуляцию.Вам часто нужны геттеры, но, как говорили другие, вы должны заставить свой геттер возвращать неизменяемую версию массива.Также постарайтесь сделать ваши классы неизменяемыми, где это возможно.Тот метод, о котором я упоминал ранее numericVector.addVector(NumericVector secondVector) вероятно, не следует изменять numericVector но верните новый NumericVector с решением.

Случай, когда это (и ОО в целом) часто терпит неудачу, - это библиотеки - когда вы Действительно хотите добавить немного функциональности в свой массив, но по-прежнему оставляете его как массив общего назначения.В этом случае Java обычно вообще не занимается инкапсуляцией, а просто добавляет вспомогательный метод/класс, который может что-то делать с коллекцией/массивом (посмотрите на объект «Arrays» несколько отличных примеров).

Эффективный для памяти способ сделать это...

package com.eric.stackoverflow.example;

import java.util.List;

import com.google.common.collect.ImmutableList;

public class Container {
    private int numArray[] = {0, 0, 0};
    public int[] getNumArray() {
        return ImmutableList.copyOf(numArray);
    }
    public void setNumArray(int index, int value) {
        numArray[index] = value;
    }    
}

Это позволяет ImmutableList чтобы установить правильное количество элементов в резервном массиве List и уменьшает количество создаваемых промежуточных объектов.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top