Escopo de Variáveis em Java
Entenda onde uma variável Java é visível — escopo local, de instância, de classe (static) e de bloco.
O escopo de uma variável é a região do programa onde você tem permissão de usar seu nome. Um nome declarado dentro de um método não é visível em outro; um nome declarado dentro de um loop for não é visível após o loop. O compilador aplica essas regras com rigor — tente acessar um nome fora do seu escopo e o código não compilará.
Java tem três tipos principais de variáveis, cada uma com seu próprio escopo: variáveis locais (incluindo parâmetros), campos de instância e campos de classe. Este capítulo foca no escopo local, pois é ele que molda os métodos que você tem escrito.
Variáveis locais
Uma variável local é declarada dentro de um método ou bloco, e é visível apenas a partir do ponto de declaração até o final do bloco que a envolve:
public static int demo() {
int x = 1; // x is in scope from here...
System.out.println(x);
return x;
} // ...to here
// System.out.println(x); // ERROR: x not visible outside demo()Variáveis locais não são inicializadas pelo Java — você deve atribuir um valor antes de lê-las. O compilador rastreia a atribuição definida e se recusa a compilar a leitura de uma variável local possivelmente não inicializada:
public static void unassigned() {
int n;
// System.out.println(n); // ERROR: variable n might not have been initialized
n = 5;
System.out.println(n); // OK now
}Isso é diferente dos campos de instância e de classe, que o Java inicializa por padrão (números para 0, boolean para false, referências de objeto para null).
Escopo de bloco
Chaves definem um bloco. Variáveis declaradas dentro de um bloco são visíveis apenas dentro dele, incluindo blocos aninhados, mas não fora:
public static void blocks() {
int outer = 10;
if (outer > 0) {
int inner = 20;
System.out.println(outer + inner); // both visible
}
// System.out.println(inner); // ERROR: inner is out of scope here
}Isso se aplica a if, for, while, do-while, switch e a blocos { ... } simples que você escreve para agrupamento. A variável de loop de um for está no escopo apenas dentro do loop:
for (int i = 0; i < 3; i++) {
System.out.println(i);
}
// System.out.println(i); // ERROR: i is out of scopeSe você precisar do índice após o loop, declare i fora:
int i = 0;
for (; i < 3; i++) {
System.out.println(i);
}
System.out.println("stopped at " + i);Parâmetros de método
Parâmetros são variáveis locais que são inicializadas pelo chamador. Seu escopo é o corpo inteiro do método, da chave de abertura até a de fechamento:
public static int square(int n) { // n is in scope from here...
return n * n;
} // ...to hereCada chamada tem seu próprio conjunto de slots de parâmetros, portanto chamadas recursivas não interferem umas nas outras.
Sombreamento
Java proíbe declarar uma variável local com o mesmo nome de uma que já está no escopo no mesmo bloco ou em um bloco externo do mesmo método:
public static void clash() {
int x = 1;
// int x = 2; // ERROR: variable x is already defined
if (true) {
// int x = 3; // ERROR: x is in scope from the outer block
}
}Isso é mais restritivo do que o que muitas linguagens permitem — Java assume que reutilizar um nome dentro de um método é um erro. (Campos de instância e de classe podem ser sombreados por variáveis locais de mesmo nome; abordaremos isso na parte de POO do livro.)
Note que dois blocos irmãos podem reutilizar um nome, porque seus escopos não se sobrepõem — nenhum é visível dentro do outro:
public static void siblings() {
for (int i = 0; i < 3; i++) { /* first i */ }
for (int i = 0; i < 3; i++) { /* a fresh i — legal, the first is gone */ }
}Tempo de vida vs. escopo
Escopo é onde você pode usar o nome. Tempo de vida é por quanto tempo o armazenamento subjacente existe. Eles geralmente coincidem — quando uma variável local sai do escopo, seu slot desaparece — mas para objetos a distinção importa:
public static String makeName() {
String s = new String("Ada"); // s in scope here
return s;
} // s out of scope, but the String object lives onA variável s desaparece, mas o objeto String ao qual ela se referia ainda está vivo — o chamador mantém uma referência, portanto o coletor de lixo não o recolherá. Variáveis locais limitam nomes, não tempos de vida de objetos.
Um vislumbre de campos de instância e de classe
Dentro de uma classe, ao lado dos métodos, você pode declarar campos — variáveis cujo escopo é a classe inteira. Existem dois tipos:
- Campos de instância pertencem a cada objeto criado a partir da classe. Cada
Dogtem seu próprionameeage. - Campos de classe (declarados como
static) pertencem à própria classe; existe uma cópia compartilhada.
public class Counter {
private static int totalCreated = 0; // class field — one shared
private int value = 0; // instance field — one per Counter
public Counter() {
totalCreated++;
}
public void inc() {
value++; // refers to this instance's field
}
}Não vamos nos aprofundar aqui — Classes e Objetos apresenta a própria classe, e Atributos de Classe aborda campos de instância adequadamente. Em resumo: campos são visíveis em toda a classe; variáveis locais são visíveis apenas no bloco em que foram declaradas.
Um exemplo prático
O que vem a seguir
O escopo diz onde um nome vive. A próxima questão — depois de passar um argumento para um método — é o que o método realmente possui. O capítulo sobre passagem por valor esclarece a confusão mais comum em Java: o que realmente acontece quando você passa uma referência de objeto.