Boas praticas

Qual seria a melhor forma de declarar um atributo em java:

→ Utilizando wrappers
→ Utilizando tipos primitivos

Estou curtindo bastante utilizar wrappers em classes (em minha opiniao faz mais sentido) e tipos primitivos utilizar em métodos, metodos statico ou na classe principal (main).

Em opiniao de voces, qual seria a forma adequada de utilizar cada um? E se é uma boa pratica o que citei acima.

Desculpe se parecer muito rude, mas essa distinção não faz o menor sentido.

O que vc precisa é entender as diferenças entre eles, e aí analisar o que faz mais sentido ou não para cada caso.

O tipo primitivo ocupa menos espaço e em geral é mais eficiente (já que o wrapper precisa criar uma instância, vai ocupar espaço no heap, é mais um objeto pro GC gerenciar, etc).

O wrapper nem sempre se comporta da forma que você espera. Por exemplo, o código abaixo imprime false:

Integer x = 1000;
Integer y = 1000;
System.out.println(x == y); // false

Curiosamente, se trocar o 1000 para 10, aí dá true. Isso porque os valores entre -128 e 127 são cacheados internamente, e aí eles sempre geram a mesma instância. Mas valores fora dessa faixa acabam criando uma nova instância.

Outra diferença é que um wrapper pode ser nulo. Ou seja, Integer x = null; é válido (e qualquer tentativa de usá-lo dará um NullPointerException), já int x = null; nem compila. Existem situações em que pode ser necessário que o valor seja null (por exemplo, em um campo opcional, o null indicaria que ele não foi enviado, etc). Com primitivo, vc teria que usar artifícios como -1 para indicar que o valor é inválido (supondo que o método só trabalhe com números positivos), mas o “correto” depende de cada caso.

Um motivo em que seria “obrigatório” usar wrappers é quando você tem collections (List, Set e Map), pois em Java não é possível ter um List<int> (somente List<Integer>). Aí não tem jeito, é uma limitação da linguagem e não tem pra onde fugir.

Tem também a questão da performance. Quando você usa um wrapper em contextos que esperam um primitivo (ou vice-versa), é feita uma conversão automática (o chamado autoboxing e unboxing). Por isso dá para adicionar um primitivo diretamente em uma lista (list.add(20)) ou usar um Integer diretamente em cálculos, a conversão é feita por baixo dos panos pra vc. Só que toda essa conversão tem seu custo, e dependendo do caso pode degradar a performance (e claro que deve ser avaliado caso a caso, para ver se isso faz diferença ou não para a aplicação).

Enfim, é isso tudo que deve ser avaliado em cada caso, pra saber qual usar. E aí tanto faz se é numa classe, método (estático ou não), no main, etc.


Aliás, entender o que cada coisa faz, pra daí decidir quando usar um ou outro, vale para qualquer coisa em computação. As tais “boas práticas” não são leis imutáveis que devem ser seguidas cegamente, são no máximo recomendações. Mas vc sempre tem que avaliar o contexto pra saber quando segui-las ou não, e pra isso precisa entender bem o funcionamento de cada coisa.

6 curtidas

Complementando.

É muito comum ver em tutoriais o uso dos wrappers em atributos de classes que serão mapeadas para um banco de dados relacional. O principal motivo é que as colunas de uma tabela podem ter valores nulos e uma forma de representar isso no mundo orientado a objetos é um tipo que também deve poder ser nulo. Sempre que se usar um wrapper tenha em mente que praticamente todas as operações usando wrappers e/ou literais dos tipos compatíveis haverá uma série de conversões implícitas, como o @hugokotsubo mencionou, que serão executadas para que as expressões façam sentido. A principal motivação para os wrappers é exatamente quando você precisa de “um tipo primitivo que pode ser que seja nulo”, não porque é mais bonito ou coisas do tipo. Outro uso mais “exotérico” pra quem só molhou as canelas na linguagem Java é com genéricos, reflexão e metaprogramação. Por exemplo, existe a classe Void que representa o “tipo” void (https://www.baeldung.com/java-void-type).

4 curtidas

Complementando, outra diferença é a alocação de memória e também a serialização.
Ao serializar um objeto Boolean vão trafegar 47 bytes.
Ao serializar um tipo primitivo boolean vai trafegar somente 1 byte.

Tem também a questão de casting, se você tiver por exemplo as seguintes variáveis:

byte  a = 1;
short b = 2;
int   c = 3;
long  d = 4

Com tipos primitivos você pode tranquilamente fazer isso:

d = c; // um long ocupa 2 ints, ou 8 bytes, então posso armazenar o valor de um int em um long
c = b; // um int ocupa 2 shorts, ou 4 bytes, então posso armazenar o valor de um short em um int
b = a; // um short ocupa 2 bytes, então posso armazenar o valor de um byte em um short

Ou isso:

a = (byte)  b; // um short ocupa 2 bytes, então posso fazer um cast de short para byte desprezando o byte mais significativo do short eventualmente truncando o seu valor
b = (short) c; // um int ocupa 4 bytes, então posso fazer um cast de int para short desprezando os 2 bytes mais significativos do int eventualmente truncando o seu valor
c = (int)   d; // um long ocupa 8 bytes, então posso fazer um cast de long para int desprezando os 4 bytes mais significativos do long eventualmente truncando o seu valor

Entretanto esse tipo de cast não é possível de fazer com as wrapper class pois não há relação de herança entre elas, ou seja, elas são incompatíveis entre si.

Então se você tiver:

Byte    a = 1;
Short   b = 2;
Integer c = 3;
Long    d = 4

Você não poderá fazer isso:

d = c;
c = b;
b = a;

Muito menos isso:

a = (Byte)    b;
b = (Short)   c;
c = (Integer) d;

Você terá erros de compilação pois a classe Short não estende a classe Byte, a classe Integer não estende a classe Short e a classe Long não estende a classe Integer.

5 curtidas

Pesquisando mais fundo, pois chamou minha atenção esse assunto, mudei minha visao totalmente com wrappers e primitivos.

Em comparação ao conectar num banco de dados, então não seria obrigatório utilizar wrappers? Pois no banco de dados, alguma coluna pode ser null.

O @davidbuzatto já comentou acima que se uma coluna pode ser nula, não tem muito o que fazer a não ser usar um wrapper.

Mas se a coluna for NOT NULL, poderia muito bem usar primitivos sem problema.

2 curtidas

Se a coluna for NOT NULL, não teria necessidade de usar wrapper no código Java.

2 curtidas

Nesse caso o processo é um pouco mais chato:

Integer x = 10;
byte b = x.byteValue(); // b = x; não compila
short c = x.shortValue(); // c = x; não compila
long d = x; // ok
2 curtidas

Exatamente, inclusive editei meu post colocando exemplos.

2 curtidas