Initialisierung von Schnittstellenkonstanten
2 Kommentar(e). Veröffentlicht von Christian Ullenboom am Donnerstag, Mai 28, 2009.Eine Schnittstelle kann Attribute deklarieren, aber das sind dann immer initialisierte public final static Konstanten.
import java.util.Properties;
public interface PropertyReader
{
Properties DEFAULT_PROPERTIES = new Properties();
Properties getProperties();
}
Würden wir DEFAULT_PROPERTIES nicht mit new Properties() initialisieren, gäbe es einen Compilerfehler.
Nun stellt sich das Problem, wenn die statischen Attribute nicht einfach mit einem Objekt initialisiert werden können, sondern wenn zusätzlicher Programmcode zur Initialisierung gewünscht ist. Für unser Beispiel soll das Properties-Objekt unter dem Schlüssel date die Zeit speichern, an der die Klasse initialisiert wurde. Über statische Initialisierer ist dies jedenfalls nicht möglich.
import java.util.*;
public interface PropertyReader
{
Properties DEFAULT_PROPERTIES = new Properties();
static
^
// Hier gibt's den Compilerfehler
// "Interfaces can't have static initializers."
{
DEFAULT_PROPERTIES.setProperty( "date", new Date().toString() );
}
Properties getProperties();
}
Zwar sind statische Initialisierungsblöcke nicht möglich, aber mit zwei Tricks kann die Initialisierung erreicht werden. Eine innere anonyme Kasse ist eine Lösung:
import java.util.*;
public interface PropertyReader
{
Properties DEFAULT_PROPERTIES = new Properties() { {
setProperty( "date", new Date().toString() );
} };
Properties getProperties();
}
Diese Lösung funktioniert allerdings nur, wenn keinen primitiven Werte über extra Programmcode initialisiert werden, und wenn es nicht-finale Klassen sind, von den überhaupt Unterklassen erlaubt sind.
Mit einem anderen Trick lassen sich auch diese Hürden nehmen. Die Idee liegt in der Einführung einer inneren statischen Klasse, die wir $$ nennen wollen. Innerhalb dieser Klasse platzieren einen static-Block. Anschließend muss nur noch eine Dummy-Variable gesetzt werden, damit der Initialisierungsblock der inneren Klasse auch ausgeführt wird, wenn die Klasse geladen wird. Dazu definieren wir die Variable $. Sie ist static und final, also somit eine echte Konstante. Da leider innere Klassen und Konstanten von Schnittstellen nicht privat sein können, und so unglücklicherweise von außen zugänglich sind, geben wir ihnen die schönen kryptischen Namen $ und $$, sodass sie nicht so attraktiv erscheinen.
import java.util.*;
public interface PropertyReader
{
Properties DEFAULT_PROPERTIES = new Properties();
PropertyReaderInit $ = new $$();
static final class $$
{
static
{
DEFAULT_PROPERTIES.setProperty( "date", new Date().toString() );
}
}
Properties getProperties();
}
Innerhalb von static-Block lässt sich auf das Properties-Objekt zugreifen und somit auch die Werte eintragen. Ohne die Erzeugung des Objekts $ geht es nicht, denn andernfalls würde die Klasse $$ nicht initialisiert werden. Das Programm kann nun alle Elemente wie folgt nutzen:
import java.util.Properties;
public class SystemPropertyReaderDemo implements PropertyReader
{
@Override public Properties getProperties()
{
return System.getProperties();
}
public static void main( String[] args )
{
System.out.println( PropertyReader.DEFAULT_PROPERTIES ); // {date=Thu …
}
}
Hinweis: Aufzählugen über enum können einfacher initialisiert werden.
Labels: Insel

Die Initialisierung mit inneren anonymen Klassen kann man auch schön für Listen, Maps oder Sets verwenden und man hat es schön "Double Brace Initialization" genannte =). Habe zufällig gerade vor ein paar Tagen auch über eine solche Art der Initialisierung geschrieben.
Das mit der inneren statischen Klasse ist aber auch sehr interessant - muss man erstmal drauf kommen.
ist schon nicht übel.. eine andere Variante ist, dass die innere statische Klasse die Property vorbereitet und zurückgibt, so dass man diese nur noch zuweisen muss:
interface PropertyReader
{
Properties DEFAULT_PROPERTIES = $$.getFunnyProperty();
static final class $$
{
static Properties getFunnyProperty()
{
Properties p = new Properties();
p.setProperty( "date", new Date().toString() );
return p;
}
}
Properties getProperties();
}