Почему Java не использует инкапсуляцию с некоторыми классами?

25

Мой вопрос связан с System.inи System.outклассами (там может быть и другими , как те , в библиотеке Standard). Почему это? Разве это не плохая практика в ООП? Разве это не должно быть использовано как: System.getIn()и System.getOut()? У меня всегда был этот вопрос, и я надеюсь, что смогу найти хороший ответ здесь.

Clawdidr
источник

Ответы:

36

Определение для inи outполей внутри Systemкласса:

public final static PrintStream out;
public final static InputStream in;

Это константы. Они тоже бывают объектами, но они постоянные. Это очень похоже на класс Math:

public static final double E = 2.7182818284590452354;
public static final double PI = 3.14159265358979323846;

Или в логическом классе:

public static final Boolean TRUE = new Boolean(true);
public static final Boolean FALSE = new Boolean(false);

Или в классе Color:

public final static Color white     = new Color(255, 255, 255);
public final static Color black     = new Color(0, 0, 0);
public final static Color red       = new Color(255, 0, 0);

При доступе к общедоступной константе, которая не изменяется, нет существенного преимущества для ее инкапсуляции - концептуально или на основе производительности. Это здесь. Это не изменится.

Там нет никакой реальной разницы между Color.whiteи System.out.


источник
Ну, я не видел это таким образом, они постоянные объекты. Это довольно хорошее объяснение.
Clawdidr
1
@Clawdidr в сегодняшней Java, можно рассмотреть возможность использования их enumдля хранения ... хотя это enumбыло новшество с Java 1.5 (не вариант в 1.0 дней).
Просто из любопытства (сам я не Java-разработчик): есть ли разница между final staticи static final? Если так, то, что это?
Марьян Венема
1
@MarjanVenema без разницы, просто предпочтительный порядок модификаторов. Модификатор checkstyle check показывает предпочтительный порядок. Очевидно, кто бы ни написал эти два исходных файла в jdk-версии, я не согласился с порядком. Это просто соглашение.
:) и спасибо Михаилу, что нашли время ответить и за ссылку.
Марьян Венема
5

Настоящая причина в том, что это проблема наследия. Эти System.in,out,errконстанты были частью Java 1.0 ... и , вероятно, намного дальше. К тому времени, когда стало ясно, что у дизайна были проблемы, было слишком поздно, чтобы исправить это. Лучшее, что они могли сделать, - это добавить System.setIn,setOut,setErrметоды в Java 1.1, а затем заняться проблемами спецификации языка 1 .

Это похоже на проблему того, почему существует статический System.arraycopyметод, имя которого нарушает соглашения об именах Java.


Что касается того, является ли это «плохим дизайном» или нет, я думаю, что это так. Существуют ситуации, когда текущая обработка без OO является серьезной проблемой. (Подумайте ... как вы можете запустить одну Java-программу внутри другой, когда их требования к стандартному потоку ввода-вывода вступают в противоречие. Подумайте ... код модульного тестирования, который влечет за собой изменение потоков.)

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


1 - Интересно отметить, что System.in,out,errпеременные в JLS особо упоминаются как имеющие «особую семантику». JLS говорит, что если вы измените значение finalполя, поведение будет неопределенным ... за исключением случая с этими полями.

Стивен С
источник
2

Я считаю, что объект out является неизменным, что делает его каким-то образом безопасным (спорным) для хранения в открытом конечном статическом поле.

Многие из классов в JDK не соответствуют лучшим принципам объектно-ориентированного проектирования. Одной из причин этого является тот факт, что они были написаны почти 20 лет назад, когда объектно-ориентация только становилась основной парадигмой, и многие программисты просто не были знакомы с ними, как сейчас. Очень хороший пример плохого дизайна API - API Date & Time, на изменение которого ушло 19 лет ...

m3th0dman
источник
3
Я бы сделал это утверждение еще сильнее, публичная конечная переменная (статическая или нет) точно так же «безопасна», как геттер, и я бы предпочел неизменность над парой сеттер / геттер. Сеттеры - это почти всегда плохая идея (непреложный - это хорошо, и если он не может быть неизменным, метод, который «устанавливает», он, вероятно, тоже заслуживает небольшой бизнес-логики), Если вам нужен геттер, вы можете также сделать его публичным окончательный вариант, но не рекомендуется делать ни то, ни другое - не спрашивайте у класса его данные, попросите класс сделать что-то с его данными.
Билл К
@BillK: Да, также известный как Закон Деметры (хотя это не закон, а руководство).
слеске
2

Этот ответ велик и правдив.

Я хотел бы добавить, что в некоторых случаях компромиссы были сделаны ради удобства использования.

Объекты типа String могут создаваться без нового события, когда String не является примитивом:

String s = "Hello";

Строка, являющаяся не примитивной, должна создаваться следующим образом:

String s = new String("Hello");          // this also works 

Но компилятор допускает более короткую, меньшую OO-опцию, потому что String - безусловно, наиболее широко используемый класс в API.

Также массивы могут быть инициализированы не OO-способом:

int i[] = {1,2,3};

Как ни странно, объект является экземпляром класса или массива . Значимые массивы - это совершенно отдельный тип класса.

Массивы имеют lengthоткрытое поле, которое не является константой. Также нет документации по массивам классов. (не путать с классом Arrays или java.reflect.Array).

int a = myArray.length;    // not length()
Тулаинс Кордова
источник
6
Тебе разные вещи - new String("Hello")всегда будет создавать новый объект String. Пока String s = "Hello";буду использовать интернированный объект. Сравните: "Hello" == "Hello"может быть истиной, а new String("Hello") == new String("Hello")всегда ложной. В первом случае происходит волшебство оптимизации времени компиляции, которое невозможно с new String("Hello"). См en.wikipedia.org/wiki/String_interning
@MichaelT Я добавил немного избыточности в массивы, просто для полноты картины.
Тулаинс Кордова
2
@Tarik Я возражал против того, что «строка не является примитивом, должна создаваться так: String s = new String("Hello");», что неверно. Более короткий параметр ( String s = "Hello";) более корректен из-за интернирования строк.
2
При Stringэтом нет более «правильной» опции. Оба верны. Хотя, как говорит MichaelT, более короткий предпочтителен из-за интернирования String.
Андрес Ф.
1
@AndresF. Я изменил «менее правильно» на «меньше ОО».
Тулаинс Кордова