11 março 2009

SCJP - 03 - Atribuição, conversão e Inicialização

Putz... Eu ainda tô no terceiro capitulo! Eita troço chato sô! Tentei fuçar alguma coisa interessante pra postar nas 19 primeiras paginas do capitulo, e achei pouca coisa válida :( Bem, eu já havia começado a falar algo, muito por cima, nesse post. Mas aqui é a seção mais apropriada para se falar dessas coisas.




Algumas coisas que a gente deve sempre ter em mente:
  • literais do tipo inteiro, ou seja, coisas como 1, 2, 100, etc, são sempre transformados automaticamente pra o tipo primitivo int (como nós ja vimos no post citado).

  • literais do tipo ponto flutuante, ou seja, coisas como 3.1415, 6.02E24, 1.618 e afins são sempre transformados automaticamente para o tipo primitivo double.

  • conversões de tipos menos abrangentes para tipos mais abrangentes podem ser feitas de modo implícito pelo compliador (e isso é conhecido como widening.

  • conversões de tipos mais abrangentes para tipos menos abrangentes devem ser feitas de modo explícito. Esse tipo de conversão é chamada de narrowing. Não fazer isso implica num erro de compilação, com mensagem do tipo "possível perda de precisão".

    Vamos tentar ser um pouco mais claros... Os seguintes trechos de codigo geram erro de compilação:

    int a = 0;
    byte teste = a;
    

    double a = 0.0;
    float b = a;
    

Claro que vocês sabem como se faz uma conversão de tipo (casting) em java, né? Ah bom :D

int a = 0;
byte teste = (int) a;

double a = 0.0;
float b = (float) a;

Oh tio... Se essas conversões automaticas ai são feitas com os literais, como eu inicializo eles então? Para valores inteiros, ou seja, para os short, byte, int e long (esqueci de algum?), tudo é feito normalmente, tomando cuidado apenas com a faixa de valores que cada um desses tipos primitivos suporta.

short a = 127;
byte b = '\"';
int c  = 123456789;
long d = 12212321321321321;

Mas com os numeros de ponto flutuante, a coisa ja muda de figura. Observe o codigo abaixo:

float a = 3.14F; //não gera erro de compilação...
double b = 3.14;  //também não gera erro de compilação...
float c  = 3.14;  //gera erro de compilação...
float d  = b;     //eu preciso mesmo falar alguma coisa? oO

Bem, e se você não inicializar suas variáveis? Bem, se você nunca usar uma variável declarada, com certeza o Eclipse vai te dar um alerta, com aquela linha amarela embaixo da variavel e a mensagem "variable never is read" ou algo parecido. Caso contrário, a depender do escopo (e não deve ser a primeira vez que você ouve falar nessa palavra mágica), o compilador age de uma forma. Se as variáveis em questão forem variáveis de instância (primitivas ou de referência), elas são inicializadas com valores padrão. Algo parecido com:

byte varByte     = 0;
short varShort   = 0;
int varInt       = 0;
long varLong     = 0;
float varFloat   = 0.0;
double varDouble = 0.0;
Object varObject = null;

Object[] arrayObject = null;

Quando você instancia um array de objetos, todos os seus elementos também recebem valores default, a menos que você os inicialize explicitamente.

Agora... Quando suas variáveis forem locais, independente de serem primitivas ou de referência, elas realmente precisam ser inicializadas. Não sou eu quem estou dizendo, é o compilador quem reclama, então se isso é inconveniente pra você, vai reclamar com a mãe do nerd que criou a linguagem, ok? :) Com os arrays locais, você deve instanciá-lo (com certeza), como qualquer outra variável de referência, mas não necessariamente inicializar os seus elementos: pelo menos isso é feito também de modo implicito.

E agora, pra você parar de me encher o saco sobre o tamanho desse post, vamos falar bem rápido sobre atribuições. Primeiro, abra este link e dê uma lida rápida. Se quiser, apenas olhe para as figuras. Pronto? Ok.

int a = 1;
int b = a;

Quando você atribui uma variável primitiva a outra, ocorre uma deep copy. O valor da primitiva à esquerda é copiada e colada na área de memória ocupada pela primitiva da direita. Então, quando vc altera o valor da primitiva da direita, nada acontece com a primitiva da esquerda.

Object a = new Object();
Object b = a;

Quando você atribui uma variável de referência a outra, ocorre uma shallow copy. O valor da referência da direita (que no final das contas é um endereço de memória - a localização do objeto na heap) é copiado e colado na area de memória ocupada pela variavel de referência da direita. No final da atribuição, ambas as variáveis estão referenciando o mesmo objeto, então quando você altera o objeto referenciado pela variavel da direita, isso reflete no objeto referenciado pela variavel da esquerda (são o mesmo, até você alterar a referência de um deles).

String a = "Vai catar coquinho, seu nerd!";
String b = a;

Por incrível que pareça, não, isso não é uma shallow copy. É uma lazy copy. Sim, isso mesmo. A princípio, elas apontam para a mesma string (shallow copy). Mas quando você usa qualquer uma das referências para alterar a string, a JVM faz uma deep copy, ou seja, atualiza aquela referência para um novo objeto na heap. Não acredita em mim? então tá. Use o depurador da sua IDE pra avaliar o endereço de memória da variável de referência antes e depois de você mexer nela :)


Creative Commons License
Esta obra está licenciada sob uma Licença Creative Commons.
Comentários
0 Comentários

0 comments:

Postar um comentário

Regras são chatas, mas...

- Seu comentário precisa ter relação com o assunto do post;
- Em hipótese alguma faça propaganda de outros blogs ou sites;
- Não inclua links desnecessários no conteúdo do seu comentário;
- Se quiser deixar sua URL, comente usando a opção OpenID;
- CAIXA ALTA, miguxês ou erros de ortografia não serão tolerados;
- Ofensas pessoais, ameaças e xingamentos não são permitidos;