Scope delle variabili in Java
Scopri dove una variabile Java è visibile: scope locale, di istanza, di classe (static) e di blocco.
Lo scope di una variabile è la regione del programma in cui puoi usarne il nome. Un nome dichiarato all'interno di un metodo non è visibile da un altro; un nome dichiarato all'interno di un ciclo for non è visibile dopo il ciclo. Il compilatore applica queste regole in modo rigoroso — se provi a usare un nome al di fuori del suo scope, il codice non compilerà.
Java ha tre principali tipi di variabili, ciascuno con il proprio scope: variabili locali (inclusi i parametri), campi di istanza e campi di classe. Questo capitolo si concentra sullo scope locale, poiché è quello che caratterizza i metodi che hai scritto finora.
Variabili locali
Una variabile locale è dichiarata all'interno di un metodo o di un blocco, ed è visibile solo dal punto di dichiarazione fino alla fine del blocco che la contiene:
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()Le variabili locali non vengono inizializzate da Java — devi assegnare un valore prima di leggerle. Il compilatore tiene traccia dell'assegnazione definitiva e rifiuta di compilare la lettura di una variabile locale potenzialmente non assegnata:
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
}Questo è diverso dai campi di istanza e di classe, che Java inizializza con valori predefiniti (numeri a 0, boolean a false, riferimenti a oggetti a null).
Scope di blocco
Le parentesi graffe definiscono un blocco. Le variabili dichiarate all'interno di un blocco sono visibili solo al suo interno, compresi i blocchi annidati, ma non all'esterno:
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
}Questo vale per if, for, while, do-while, switch e per i blocchi { ... } che scrivi per raggruppare il codice. La variabile di ciclo di un for è nello scope solo all'interno del ciclo:
for (int i = 0; i < 3; i++) {
System.out.println(i);
}
// System.out.println(i); // ERROR: i is out of scopeSe hai bisogno dell'indice dopo il ciclo, dichiara i all'esterno:
int i = 0;
for (; i < 3; i++) {
System.out.println(i);
}
System.out.println("stopped at " + i);Parametri dei metodi
I parametri sono variabili locali che vengono inizializzate dal chiamante. Il loro scope è l'intero corpo del metodo, dalla parentesi graffa di apertura a quella di chiusura:
public static int square(int n) { // n is in scope from here...
return n * n;
} // ...to hereOgni chiamata riceve il proprio set di slot per i parametri, quindi le chiamate ricorsive non si interferiscono tra loro.
Shadowing
Java vieta la dichiarazione di una variabile locale con lo stesso nome di una già nello scope nel medesimo blocco o in un blocco esterno dello stesso metodo:
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
}
}Questo è più restrittivo di quanto molti linguaggi consentano — Java presuppone che riutilizzare un nome all'interno di un metodo sia un errore. (I campi di istanza e di classe possono essere oscurati da variabili locali con lo stesso nome; lo vedremo nella parte OOP del libro.)
Nota che due blocchi fratelli possono riutilizzare un nome, perché i loro scope non si sovrappongono — nessuno è visibile all'interno dell'altro:
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 */ }
}Lifetime vs. scope
Lo scope indica dove puoi usare il nome. Il lifetime indica per quanto tempo esiste lo storage sottostante. Di solito coincidono — quando una variabile locale esce dallo scope, il suo slot scompare — ma per gli oggetti la distinzione è importante:
public static String makeName() {
String s = new String("Ada"); // s in scope here
return s;
} // s out of scope, but the String object lives onLa variabile s scompare, ma l'oggetto String a cui faceva riferimento è ancora vivo — il chiamante ha un riferimento, quindi il garbage collector non lo recupererà. Le variabili locali limitano i nomi, non i lifetime degli oggetti.
Un'introduzione ai campi di istanza e di classe
All'interno di una classe, insieme ai metodi, puoi dichiarare campi — variabili il cui scope è l'intera classe. Esistono due varianti:
- I campi di istanza appartengono a ogni oggetto creato dalla classe. Ogni
Dogha il proprionameeage. - I campi di classe (dichiarati
static) appartengono alla classe stessa; esiste una sola copia condivisa.
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
}
}Non approfondiremo qui — Classi e Oggetti introduce la classe stessa, e Attributi di Classe tratta i campi di istanza in modo appropriato. In sintesi: i campi sono visibili ovunque nella classe; le variabili locali sono visibili solo nel blocco in cui sono dichiarate.
Un esempio pratico
Cosa c'è dopo
Lo scope ti dice dove vive un nome. La prossima domanda — una volta passato un argomento a un metodo — è cosa ha effettivamente il metodo. Il capitolo sul passaggio per valore chiarisce la confusione più comune in Java: cosa succede davvero quando passi un riferimento a un oggetto.