Почему переменные экземпляра в Java всегда закрыты? - PullRequest
20 голосов
/ 02 октября 2011

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

http://www.tutorialspoint.com/java/java_encapsulation.htm

У меня 2 запроса:

  1. Почему переменные экземпляра закрыты? Почему не публично?
  2. Что, если переменные экземпляра становятся открытыми и доступны напрямую? Видим ли мы какие-либо ограничения?

Можете ли вы объяснить на примере, что пойдет не так, если переменные экземпляра объявлены как публичные в классе в Java?

Ответы [ 9 ]

36 голосов
/ 02 октября 2011

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

Использование методов позволит вам, например, ограничить доступ только для чтения, т. Е. Поле может быть прочитано, но не записано, если нет установщика. Это было бы невозможно, если бы поле было общедоступным.

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

Если phone был приватным:

Рассмотрим этот случай:

class Address {
  private String phone;

  public void setPhone(String phone) {
    this.phone = phone;
  }
}

//access:
Address a = new Address();
a.setPhone("001-555-12345");

Если бы мы начали с класса, подобного этому, и позже потребовалось бы выполнить проверку на phoneNumber (например, некоторую минимальную длину, только цифры и т. Д.), Вам просто нужно изменить установщик:

class Address {
  private String phone;

  public void setPhone(String phone) {
    if( !isValid( phone) ) { //the checks are performed in the isValid(...) method
     throw new IllegalArgumentException("please set a valid phone number");
    }

    this.phone = phone;
  }
}

//access:
Address a = new Address();
a.setPhone("001-555-12345"); //access is the same

Если phone был публичным:

Кто-то может установить phone так, и вы ничего не можете с этим поделать:

Address a = new Address();
a.phone="001-555-12345";

Если вы теперь хотите принудительно выполнить проверки достоверности, вам придется сделать это закрытым, и тот, кто написал вышеупомянутые строки, должен будет изменить вторую строку на эту:

a.setPhone("001-555-12345");

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

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

10 голосов
/ 02 октября 2011

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

7 голосов
/ 02 октября 2011

Во-первых, неверно, что все переменные экземпляра являются частными. Некоторые из них защищены, что сохраняет инкапсуляцию.

Общая идея инкапсуляции заключается в том, что класс не должен раскрывать свое внутреннее состояние. Он должен использовать его только для выполнения своих методов. Причина в том, что у каждого класса есть так называемое «пространство состояний». То есть набор возможных значений для его полей. Он может контролировать свое пространство состояний, но если он его выставит, другие могут перевести его в недопустимое состояние.

Например, если у вас есть два логических поля, и класс может функционировать правильно только в 3 случаях: [false, false], [false, true] и [true, false]. Если вы сделаете поля открытыми, другой объект может установить [true, true], не зная внутренних ограничений, и следующий метод, вызванный для исходного объекта, вызовет неожиданные результаты.

4 голосов
/ 02 октября 2011

Сделать переменные экземпляра общедоступными или закрытыми - это компромисс дизайнер делает при объявлении классов. Делая экземпляр переменные public, вы выставляете детали реализации класса, тем самым обеспечивая более высокую эффективность и краткость выражения при возможные затраты, препятствующие будущим усилиям по обслуживанию. От скрывая детали внутренней реализации класса, у вас есть Потенциал изменить реализацию класса в будущем без нарушения кода, который использует этот класс.

Oracle White Paper

2 голосов
/ 02 октября 2011

Как уже указывалось несколькими авторами, переменные экземпляра не обязательно должны быть private, но они обычно по крайней мере не сделаны public, чтобы сохранить инкапсуляцию.

Я видел пример в (я думаю) Чистом коде, который очень хорошо это иллюстрирует. Если я правильно помню, это был комплексный тип (как в a+bi) типа; в любом случае, что-то очень похожее, у меня нет книги под рукой. В нем представлены методы для получения значения действительной и мнимой частей, а также метод для установки значения экземпляра. Большим преимуществом этого является то, что он позволяет полностью заменить реализацию, не нарушая никаких потребителей кода. Например, комплексные числа могут храниться в одной из двух форм: в виде координат на комплексной плоскости (a+bi) или в полярной форме (φ и |z|). Сохраняя формат внутреннего хранилища, деталь реализации позволяет вам переключаться вперед и назад, все еще выставляя число в обеих формах, что позволяет пользователю выбирать класс в зависимости от того, какая операция удобнее для него в данный момент.

В других ситуациях у вас может быть набор связанных полей, например, поле x должно иметь определенные свойства, если поле y попадает в заданный диапазон. В качестве упрощенного примера можно привести, где x должно находиться в диапазоне от y до y+z, для числовых значений и некоторого произвольного значения z. Предоставляя методы доступа и мутаторы, вы можете навязать эту связь между двумя значениями; если вы выставите переменные экземпляра напрямую, инвариант немедленно распадется, так как вы не можете гарантировать, что кто-то не установит одно, но не другое, или установит их так, чтобы инвариант больше не выполнялся.

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

1 голос
/ 02 октября 2011

В традиционном объектно-ориентированном дизайне класс будет инкапсулировать как данные (переменные), так и поведение (методы). Наличие личных данных даст вам гибкость в отношении того, как реализовано поведение, поэтому, например, объект может хранить список значений и иметь метод getAverage (), который вычисляет и возвращает среднее значение этих значений. Позже вы можете оптимизировать и кэшировать вычисленное среднее значение в классе, но контракт (т. Е. Методы) менять не нужно.

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

ОБНОВЛЕНИЕ: пример, приведенный в ссылке в вопросе, является прекрасным примером этой вырожденной инкапсуляции. Я понимаю, что автор пытается привести простой пример, но при этом он не дает никакой реальной выгоды от инкапсуляции (по крайней мере, в коде примера).

0 голосов
/ 21 июня 2016

Для пользователя класса

Мы, которые используют ide как eclipse, netbins ....., увидели, что он предлагает нам открытый метод, поэтому, если создатель класса предоставляет getter и setter для закрытой переменной экземпляраВам не нужно запоминать имя переменной.просто напишите set, нажмите ctrl + пробел, вы получите весь метод установки, созданный создателем этого класса, и выберите нужный метод для установки значения переменной.

Для создателя класса

Иногда вам нужноуказать некоторую логику, чтобы установить значение переменной."Предположим, у вас есть целочисленная переменная, которая должна хранить 0

0 голосов
/ 20 июля 2013

Сохранение приватности полей имеет много преимуществ, как указано выше.Следующим лучшим уровнем является сохранение конфиденциальности пакетов с использованием уровня доступа по умолчанию в java.

Уровень по умолчанию позволяет избежать беспорядка в вашем собственном коде и не позволяет клиентам вашего кода устанавливать недопустимые значения.

0 голосов
/ 02 октября 2011
  1. Потому что, если вы измените структуру класса (удалив поля и т. Д.);это приведет к ошибкам.Но если у вас есть метод getX(), вы можете вычислить нужное значение там (если поле было удалено).

  2. Тогда у вас есть проблема, что класс не знает, если что-то изменилосьи не может гарантировать целостность.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...