Отличный вопрос. Я не думаю, что приведенные вами примеры нарушают инкапсуляцию, по крайней мере, строго.
Первый пример, который вы привели, - это константа PI, а второй - это пример, который обеспечивает доступ к константе out в System; предположительно, чтобы использовать какой-то код, например System.out.println("HelloWorld!");
. Как уже упоминалось другими, PI
фактически является просто константой, и у пользователей константы нет средств, с помощью которых можно изменить или повлиять на значение. Пользователям PI
по-прежнему необходимо ссылаться на константу (которая составляет здесь API) в своем коде. Если бы PI
был изменен (маловероятно, но кто знает), пользователи были бы в безопасности от последствий этого изменения, потому что им все равно пришлось бы перекомпилировать код.
Полезный способ подумать об инкапсуляции - это подумайте, что потребуется для нарушения инкапсуляции. Эффективное Java 3-е издание хорошо описывает это в правиле 16, предполагая, что без надлежащей инкапсуляции «вы не можете изменить представление, не изменяя API, вы не можете принудительно применять инварианты, и вы не можете предпринять вспомогательные действия, когда поле доступ. "
Очень явным нарушением вышеизложенного будет следующий класс (также в пункте 16 действующего Java):
class Point {
public double x;
public double y;
}
Все поля опубликованы c и пользователи этого API будут вынуждены использовать поля напрямую. Если позже автор решил добавить некоторую проверку достоверности до того, как можно будет получить доступ или изменить x
или y
, то это было бы невозможно сделать без потенциального нарушения работы существующих клиентов. API должен быть значительно изменен и, вероятно, нарушит поведение нижестоящих пользователей.
Теперь давайте посмотрим на второй предоставленный вами пример:
public class System
{
. . .
public static final PrintStream out = . . .;
. . .
}
Это похоже на пример PI
выше, но есть ключевое отличие: рассматриваемое поле является PrintStream. Хотя само поле out
является явной частью этого API, которую теперь может быть трудно изменить (теперь, когда оно открыто, клиенты будут использовать и полагаться на него), тип PrintStream
- это класс, который на самом деле интересен здесь: пользователи будут ссылаться на методы класса. Здесь у нас есть ключевая функциональность в API PrintStream
, которая может развиваться с течением времени без прерывания использования. Также возможно, что в будущем константа out
будет изменена для обозначения другого подкласса PrintStream
, и пользователи API не будут затронуты.
Надеюсь, что это поможет.