Usando classes Wrapper
As classes wrapper são usados para 2 propósitos principais:
- O primeiro propósito é fortalecer um mecanismo para empacotar valores primitivos em um tipo objeto, de modo que seja permitido ser inclusivos em atividades reservadas a objetos, como ser retomados por métodos que tenham como valor de retorno variáveis de referência ou adicionados a conjuntos.
- O outro propósito é fortalecer um conjunto de funções utilitárias para tipos primitivos. A maior parte dessas funções está relacionada a várias convenções de tipos primitivos em objetos String e vice-versa, além de tipos primitivos em objetos String para bases diferentes, como bases binárias, octais e hexadecimais.
Existe uma classe wrapper para cada tipo primitivo. Todas as classes wrapper, com exceção de Character, fornecem 2 construtores: um que utiliza como argumento o tipo primitivo que está sendo criado e outro que usa representação String do tipo sendo construído. Veja alguns exemplos.
Integer i = new Integer(41);
Integer i1 = new Integer(“52”);
Float f = new Float(3.16F);
Float f1 = new Float(“2,18”);
A classe Character fornece apenas um constructor que utiliza tipo char como argumento.
Character c = new Character(‘x’);
Métodos valueOf()
Os dois métodos static valueOf() fornecidos na maior parte das classes wrapper lhe permitirão outra abordagem para a criação de objetos wrapper.
package devmedia;
public class Executavel{
public static void main(String args[ ]){
Integer i1 = Integer.valueOf(“101011”,2); //converte 101011 em 43
Float f1 = Float.valueOf(“2.16F” );
int i2 = Integer.valueOf(“22”); //boxing
System.out.println(i1);
System.out.println(f1);
System.out.println(i2);
Integer a = Integer.valueOf(“true”); //exception
}
}
parseXxx()
Os 6 métodos parseXxx(), um para cada tipo de numérico, estão altamente relacionados ao método valueOf(), que há em todas as classes wrapper numéricas.
Analise alguns exemplos:
double a3 = Double.parseDouble(“4.06”);
Double a6 = Double.valueOf(“5.33”)
System.out.println(a6 instanceof Double); //true
long x1 = Long.parseLong(“456789”,2);
long x2 = Long.valueOf(x1);
toString()
A finalidade de toString() é dar alguma representação significativa de determinado objeto. As classes wrapper contem a versão de uma instância do método toString(), não-static e sem argumentos. Este método tem como retorno uma String como valor do tipo primitivo encapsulado no objeto. Veja um exemplo:
Double double1 = new Double("5.17");
System.out.println(double1.toString());
As classes wrapper numéricas fornecem um método sobrecarregado e static toString() que utiliza o tipo numérico primitivo apropriado: Double.toString(), Long.toString() e assim por diante, retorna uma String.
String text = Double.toString(1.95);
Sendo assim, as classes Long e Integer fornecem um terceiro método toString(). Esse é static, seu primeiro argumento é do tipo primitivo e o segundo é uma base. O argumento da base informa o método para pegar o primeiro argumento, que por padrão está na base 10, e convertê-lo para a base fornecida, retornando, como resultado, uma String. Veja a seguir:
String exemplo = "resulta em "+ Long.toString(254, 16);//resulta em fe
Autoboxing
Essa novidade do Java 5 é bem revolucionária, este recurso é conhecido por vários nomes: boxing, autoboxing, auto-unboxing, unboxing.
A partir de agora, com o recurso aprimorado e novo do Java 5, você pode utilizar:
Integer i = new Integer(549);
int a = i.intValue();
a++;
i = new Integer(a);
Implicitamente (“por debaixo dos panos”), o compilador faz o unboxing e a reatribuição automáticamente.
Sobrecarga com ampliação, boxing e vargars
Quando uma classe possui métodos sobrecarregados, uma das tarefas do compilador é determinar qual é o método a ser utilizado. Observe o exemplo abaixo:
package devmedia;
public class Sobrecarga {
static void ir(double a){
System.out.println("double version.");
}
/**
* @param args
*/
public static void main(String[] args) {
byte bt = 2;
short sh = 2;
float fl = 2.0f;
long lg = 2;
ir(bt);
ir(sh);
ir(fl);
ir(lg);
}
}
Saída:
double version.
double version.
double version.
Adicionaremos boxing ao exemplo anterior.
package devmedia;
public class Sobrecarga {
static void ir(Integer a){
System.out.println("Integer version.");
}
static void ir(Long l){
System.out.println("Long version.");
}
static void ir(Double d){
System.out.println("Double version.");
}
/**
* @param args
*/
public static void main(String[] args) {
int bt = 2;
double sh = 2;
long lg = 2;
ir(bt);
ir(sh);
ir(lg);
}
}
Saída:
Integer version.
Double version.
Long version.
No exemplo acima, ocorre boxing para as 3 invocações. O int é convertido para Integer, double é convertido para Double e o long é convertido para Long.
Foi decido pelas especificações que a regra mais importante deveria ser que os códigos preexistentes devem continuar funcionando como antes, então, uma vez que o recurso de ampliação já exista, um método invocado por meio da ampliação não deveria ser trocado por método recentemente criado, que utiliza boxing. Baseado nessa regra, tente supor qual o resultado do seguinte código:
package devmedia;
public class AddVarargs {
static void ir(int i,int a){
System.out.println("int - int");
}
static void ir(byte...b){
System.out.println("byte...");
}
public static void main(String[] args) {
byte x = 8;
ir(x,x);
}
}
A saída é: “int - int” porque mesmo que a chamada requeira algum tipo de conversão, o compilador escolherá o estilo antigo, ao invés do novo, mantendo os códigos já existentes mais robustos.
Analisaremos agora o que ocorre quando é preciso fazer de uma conversão. Neste caso, o compilador terá que compilar e em seguida fazer autoboxing do parâmetro, para que uma correspondência seja realizada.
package devmedia;
public class AumentaBoxing {
static void ir(Long n){//boxing
System.out.println("Long");
}
public static void main(String[] args) {
Byte x =7;
ir(x);//Não compila
}
}
Isso é muito para o compilador. Entretanto, o compilador pode fazer uma operação de boxing e depois de ampliação, no sentido de criar uma correspondência entre chamada e método.
Mini-teste
-
Dado:
class Main { static int disparador() throws Exception{ return 44; } public static void main(String args[]){ try { int x = disparador(); } catch (Exception e) { x++; }finally{ System.out.println("X é "+x); } } }
- A compilação falha.
- O valor de x é: 43
- O valor de x é: 44
- O valor de x é: 42
- O código é executado sem nenhuma saída.
-
Observe:
class Executavel { Short s = 5; Executavel ir(Executavel e){ e=null; return e; } public static void main(String args[]){ Executavel e1 = new Executavel(); Executavel e2 = new Executavel(); Executavel e3 = e1.ir(e2); e1 = null; //aqui } }
- 0
- 1
- A compilação falha.
- 2
- Não é possível saber.
- É lançada uma exceção.
-
Dado:
class Principal { int fazer(Long x, Long y){return 1;} int fazer(long...x){return 2;} int fazer(Integer x, Integer y){return 3;} int fazer(Number n, Number m){return 4;} public static void main(String args[]){ new Principal().ir(); } void ir(){ short s = 4; System.out.print(fazer(s,s)+" "); System.out.print(fazer(4,4)); } }
- 1 1
- 4 3
- 2 3
- 4 4
- 3 3
-
Dado:
class Mistery { int x =5; public static void main(String[] args) { final Mistery a1 = new Mistery(); Mistery a2 = new Mistery(); Mistery a3 = fazerMistery(a1, a2); System.out.println((a1==a3)+" "+(a1.x==a3.x)); } static Mistery fazerMistery(Mistery x, Mistery y){ final Mistery z = x; z.x=6; return z; } }
- É lançada uma exceção.
- false true
- true false
- false false
- true true
- A compilação falha.
-
Dado:
class Conversao { public static void main(String[] args) { Long l1 = new Long(789L); long l2 = Long.valueOf("193"); Long l3 = Long.valueOf("823"); long l4 = l1.longValue(); Long l5 = l1.longValue(); Long l6 = Long.parseLong("43"); long l7 = Long.parseLong("5"); } }
- Linha 4
- Linha 5
- Linha 6
- Linha 7
- Linha 8
- Linha 9
GABARITO COMENTADO
-
Resposta A
Note que no local do código a variável x é declarada. Ela é declarada dentro do bloco try/catch. Assim, seu escopo fica limitado a esse bloco. Desse modo, o único local em que o x pode ser referenciado é dentro do bloco try/catch. Uma vez que se tente utilizá-lo fora do try, um erro de compilação é lançado.
-
Resposta D
Apenas e1 estará qualificado; porém, ele também possui uma referência Short. o objeto para o qual essa referência aponta também estará qualificado para a coleta de lixo. Sendo assim, serão 2 objetos qualificados para o garbage collector.
-
Resposta B
Com relação à segunda chamada, não existem dúvidas de que a saída será 3, pois a melhor opção é boxing, de int para Integer. Com relação à primeira chamada, há, primeiramente, 2 opções: ampliar de short para long e usar varargs, ou fazer boxing de short para Short e em seguida ampliar para Number. A segunda opção é escolhida. Então, boxing e ampliação são possíveis, mas a recíproca não é verdadeira.
-
Resposta E
Com relação à segunda chamada, não existem dúvidas de que a saída será 3, pois a melhor opção é boxing, de int para Integer. Com relação à primeira chamada, há, primeiramente, 2 opções: ampliar de short para long e usar varargs, ou fazer boxing de short para Short e em seguida ampliar para Number. A segunda opção é escolhida. Então, boxing e ampliação são possíveis, mas a recíproca não é verdadeira.
-
Resposta E
O método valueOf() tem como retorno uma referência. Os métodos longValue() e parseLong() retornam primitivos. Só não existe necessidade de conversão quando houver paridade entre o tipo de retorno e a atribuição. A partir do Java 5 a conversão é implicita e é chamada de autoboxing.
Espero que possam aproveitar esse artigo, bons estudos e até a próxima!
Leia todos artigos da série
- Desmistificando a Certificação SCJP6 - Parte I
- Desmistificando a Certificação SCJP6 - Parte II
- Desmistificando a Certificação SCJP6 - Parte III
- Desmistificando a Certificação SCJP6 - Parte IV - Parte 1
- Desmistificando a Certificação SCJP6 - Parte IV - Parte 2
- Desmistificando a Certificação SCJP6 - Parte V - Parte 1/3
- Desmistificando a Certificação SCJP6 - Parte V - Parte 2/3
- Desmistificando a Certificação SCJP6 - Parte V - Parte 3/3
- Desmistificando a Certificação SCJP6 - Parte VI - Parte 1/3
- Desmistificando a Certificação SCJP6 - Parte VI - Parte 2/3
- Desmistificando a Certificação SCJP6 - Parte VI - Parte 3/3
- Desmistificando a Certificação SCJP6 - Parte VII - Parte 1/3
- Desmistificando a Certificação SCJP6 - Parte VII - Parte 2/3
- Operadores apropriados - Desmistificando a Certificação SCJP6 - Parte VIII - Parte 1
- Instruções if e switch - Desmistificando a Certificação SCJP6 - Parte VIII - Parte 2
- Iteradores e loops - Desmistificando a Certificação SCJP6 - Parte VIII - Parte 3
- Mini-Teste: Desmistificando a Certificação SCJP6 - Parte VIII - Parte 4 - A
- Mini-Teste: Desmistificando a Certificação SCJP6 - Parte VIII - Parte 4 - B
- Tratamento de Exceções em Java: Desmistificando a Certificação SCJP6 - Parte IX - Parte 1
- Definindo Exceções em Java: Desmistificando a Certificação SCJP6 - Parte IX - Parte 2
- Assertivas Java: Desmistificando a Certificação SCJP6 - Parte IX - Parte 3
- Mini-Teste: Desmistificando a Certificação SCJP6 - Parte IX - Parte 4
- String: Desmistificando a Certificação SCJP6 - Parte X - Parte 1
- StringBuilder/StringBuffer e File: Desmistificando a Certificação SCJP6 - Parte X - Parte 2
- Combinação de Classes E/S: Desmistificando a Certificação SCJP6 - Parte X - Parte 3
- Utilizando Arquivos e Diretórios: Desmistificando a Certificação SCJP6 - Parte X - Parte 4
- Console e Serialização: Desmistificando a Certificação SCJP6 - Parte X - Parte 5
- Mini-teste: Desmistificando a Certificação SCJP6 - Parte X - Parte 6 A
- Mini-teste: Desmistificando a Certificação SCJP6 - Parte X - Parte 6 B
- Expressões Regulares: Desmistificando a Certificação SCJP6 Parte XI - Parte 1
- Lista Negada e Metacaracteres: Desmistificando a Certificação SCJP6 Parte XI - Parte 2
- Metacaracteres Âncora e Busca: Desmistificando a Certificação SCJP6 Parte XI - Parte 3