9.4 Lokale Klassen
Lokale Klassen sind ebenfalls innere Klassen, die jedoch nicht einfach wie eine Eigenschaft im Rumpf einer Klasse, sondern direkt in Anweisungsblöcken von Methoden, Konstruktoren und Initialisierungsblöcken gesetzt werden. Lokale Schnittstellen sind nicht möglich.
9.4.1 Beispiel mit eigener Klassendeklaration
Im folgenden Beispiel deklariert die main(…)-Methode eine innere Klasse Snowden mit einem Konstruktor, der auf die finale Variable PRISM zugreift:
public class NSA {
public static void main( String[] args ) {
final int PRISM = 1;
int tempora = 2;
tempora++; // (*)
class Snowden {
Snowden() {
System.out.println( PRISM );
// System.out.println( tempora ); // Auskommentiert ein Compilerfehler
}
}
new Snowden();
}
}
Die Deklaration der lokalen Klasse Snowden wird hier wie eine Anweisung eingesetzt. Ein Sichtbarkeitsmodifizierer ist bei lokalen Klassen ungültig, und die Klasse darf keine Klassenmethoden und allgemeinen statischen Variablen deklarieren (finale Konstanten schon).
Jede lokale Klasse kann auf Methoden der äußeren Klasse zugreifen. Zusätzlich kann sie auf lokale Variablen und Parameter zugreifen, allerdings nur dann, wenn die Variablen final sind. Dabei müssen finale Variablen nicht zwingend mit dem Modifizierer final gekennzeichnet werden, um final zu sein. Gibt es keinen Schreibzugriff auf Variablen, sind sie effektiv final. Die Variable PRISM ist explizit mit dem Modifizierer final markiert, also kann die innere Klasse darauf zugreifen. tempora ist nicht final (tempora++ ist ein Schreibzugriff), und daher führt ein Lesezugriff in der inneren Klasse bei println(tempora) zu einem Compilerfehler – Eclipse meldet: »Local variable tempora defined in an enclosing scope must be final or effectively final«. Im Beispiel kann das einfach getestet werden: Wird die Zeile (*) mit tempora++; auskommentiert, so ist tempora effektiv final, und Snowden kann auf tempora zugreifen.
Liegt die innere Klasse in einer statischen Methode, kann sie keine Objektmethoden der äußeren Klasse aufrufen.
9.4.2 Lokale Klasse für einen Timer nutzen
Damit die Beispiele etwas praxisnäher werden, wollen wir uns anschauen, wie ein Timer wiederholende Aufgaben ausführen kann. Die Java-Bibliothek bringt hier schon alles mit: Es gilt, ein Exemplar von java.util.Timer() zu bilden und der Objektmethode scheduleAtFixedRate(…) ein Exemplar vom Typ TimerTask zu übergeben. Die Klasse TimerTask schreibt eine abstrakte Methode run() vor, in die der nebenläufige und regelmäßig abzuarbeitende Programmcode gesetzt wird.
Nutzen wir das für ein Programm, das uns sofort und regelmäßig daran erinnert, wie wichtig doch Sport ist:
public class SportReminder {
public static void main( String[] args ) {
class SportReminderTask extends TimerTask {
@Override public void run() {
System.out.println( "Los, beweg dich, du faule Wurst!" );
}
}
new Timer().scheduleAtFixedRate( new SportReminderTask(),
0 /* ms delay */,
1000 /* ms period */ );
}
}
Unsere Klasse SportReminderTask, die TimerTask erweitert, ist direkt in main(…) deklariert. Das erzeugte Exemplar kommt später in scheduleAtFixedRate(…), und los rennt der Timer, um uns jede Sekunde an die Wichtigkeit von Bewegung zu erinnern. Der Vorteil der lokalen Klassendeklaration ist, dass sie bis auf main(…) kein anderer sehen kann.