Por que Java não utiliza encapsulamento com algumas classes?

25

Minha pergunta está relacionada a System.ine System.outclasses (pode haver outras como as da biblioteca Padrão). Por que é que? Isso não é uma prática ruim em POO? Não deve ser usado como: System.getIn()e System.getOut()? Eu sempre tive essa pergunta e espero encontrar uma boa resposta aqui.

Clawdidr
fonte

Respostas:

36

A definição para os campos ine outdentro da Systemclasse são:

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

Estas são constantes. Eles também são objetos, mas são constantes. É muito parecido com a classe Math:

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

Ou na classe booleana:

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

Ou na classe 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);

Ao acessar uma constante pública que não muda, não há uma vantagem significativa para encapsulá-la - conceitualmente ou com base no desempenho. Está lá. Isso não vai mudar.

Não há diferença real entre Color.whitee System.out.


fonte
Bem, eu não vi dessa maneira, eles são objetos constantes. Essa é uma boa explicação.
Clawdidr 12/08
1
@Clawdidr no Java de hoje, pode-se considerar usar o enumpara mantê-los ... embora enumfosse novo no Java 1.5 (não é uma opção nos 1,0 dias).
Apenas por curiosidade (não sou desenvolvedor de Java): existe uma diferença entre final statice static final? Se assim for, o que é?
Marjan Venema
1
@MarjanVenema não há diferença, apenas a ordem preferida dos modificadores. checkstyle modifier check mostra a ordem preferida. Aparentemente, quem escreveu esses dois arquivos de origem na versão jdk não concordei com o pedido. É apenas uma convenção.
:) e obrigado Michael, por reservar um tempo para responder e pelo link.
Marjan Venema
5

A verdadeira razão é que esse é um problema herdado. As System.in,out,errconstantes faziam parte do Java 1.0 ... e provavelmente muito mais atrás. Quando ficou claro que o design apresentava problemas, era tarde demais para corrigi-lo. O melhor que eles puderam fazer foi adicionar os System.setIn,setOut,setErrmétodos no Java 1.1 e depois lidar com os problemas de especificação de linguagem 1 .

Isso é semelhante à questão de por que existe um System.arraycopymétodo estático cujo nome viola as convenções de nomenclatura Java.


Quanto a saber se isso é "design inadequado" ou não, acho que é. Há situações em que o manuseio atual sem OO é um problema sério. (Pense ... como você pode executar um programa Java dentro de outro quando seus requisitos de fluxo de "E / S padrão" entrarem em conflito. Pense ... código de teste de unidade que implica a alteração dos fluxos.)

No entanto, também posso me relacionar com o argumento de que a maneira atual de fazer as coisas é mais conveniente em muitos casos.


1 - É interessante notar que as System.in,out,errvariáveis ​​recebem menção especial no JLS como tendo "semântica especial". O JLS diz que se você alterar o valor de um finalcampo, o comportamento é indefinido ... exceto no caso desses campos.

Stephen C
fonte
2

Eu acredito que o objeto out é imutável, o que o torna de alguma forma seguro (isso é discutível) por ser mantido em um campo estático final público.

Muitas das classes no JDK não respeitam os melhores princípios de design orientado a objetos. Uma razão para isso é o fato de terem sido escritas há quase 20 anos, quando a Orientação a Objetos estava apenas emergindo como um paradigma convencional e muitos programadores simplesmente não estavam familiarizados com elas como estão agora. Um exemplo muito bom de mau design da API é a API Date & Time, que levou 19 anos para mudar ...

m3th0dman
fonte
3
Eu tornaria a declaração ainda mais forte, uma variável final pública (estática ou não) é exatamente tão "segura" quanto um getter e eu preferiria a imutibilidade sobre um par setter / getter. Setters são sempre uma péssima idéia (Imutável é bom - e se não puder ser imutável o método que "Sets" provavelmente também merece um pouco de lógica de negócios), se você precisar de um getter, poderá publicá-lo final, mas não é preferível fazer um dos dois - não peça dados a uma classe, peça a ela que faça algo com seus dados.
Bill K
@ BillK: Sim, também conhecida como Lei de Demeter (embora não seja uma lei, mas uma diretriz).
sleske
2

Essa resposta é ótima e verdadeira.

Eu queria acrescentar que, em alguns casos, foram feitos compromissos em prol da usabilidade.

Objetos do tipo String podem ser instanciados sem um evento new quando String não é um primitivo:

String s = "Hello";

Sendo uma string não primitiva, deve ser instanciada assim:

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

Mas o compilador permite a opção mais curta e menos OO, porque String é de longe a classe mais usada na API.

Também as matrizes podem ser inicializadas de maneira não OO:

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

Estranho o suficiente, um objeto é uma instância de uma classe ou uma matriz . Matrizes de significado são um tipo de classe completamente separado.

As matrizes têm um lengthcampo público que não é uma constante. Além disso, não há documentação nas matrizes de classe das quais é uma instância. (para não confundir com a classe Arrays ou java.reflect.Array).

int a = myArray.length;    // not length()
Tulains Córdova
fonte
6
Há coisas diferentes - new String("Hello")sempre criará um novo objeto String. Enquanto String s = "Hello";usará o objeto internado. Compare: "Hello" == "Hello"pode ser verdade, enquanto new String("Hello") == new String("Hello")é sempre falso. Há uma mágica de otimização do tempo de compilação acontecendo no primeiro, o que não é possível new String("Hello"). Veja en.wikipedia.org/wiki/String_interning
@ MichaelT Eu adicionei alguma estranheza extra em matrizes, apenas por uma questão de completude.
Tulains Córdova
2
@Tarik eu estava objetando ao "String sendo um não primitivo, deve ser instanciado como este: String s = new String("Hello");" que está incorreto. A opção mais curta ( String s = "Hello";) está mais correta devido à internação de strings.
2
Com String, não há opção "mais correta". Ambos estão corretos. Embora, como MichaelT diz, o mais curto seja o preferido por causa da internação por String.
Andrés F.
1
@AndresF. Alterei "menos correto" para "menos OO".
Tulains Córdova