Java 8 erlaubt statische Schnittstellenmethoden
Mit Java 8, Schnittstellen können statische Methoden haben. Sie können auch konkrete Instanzmethoden haben, jedoch keine Instanzfelder.
Hier gibt es wirklich zwei Fragen:
- Warum konnten Schnittstellen in schlechten alten Zeiten keine statischen Methoden enthalten?
- Warum können statische Methoden nicht überschrieben werden?
Statische Methoden in Schnittstellen
Es gab keinen starken technischen Grund, warum Schnittstellen in früheren Versionen keine statischen Methoden hatten. Dies wird durch das Poster einer doppelten Frage gut zusammengefasst. Statische Schnittstellenmethoden wurden anfangs als kleine Sprachänderung angesehen, und dann gab es einen offiziellen Vorschlag , sie in Java 7 hinzuzufügen, der jedoch später aufgrund unvorhergesehener Komplikationen eingestellt wurde.
Schließlich führte Java 8 statische Schnittstellenmethoden sowie überschreibbare Instanzmethoden mit einer Standardimplementierung ein. Sie können jedoch immer noch keine Instanzfelder haben. Diese Funktionen sind Teil der Unterstützung für Lambda-Ausdrücke. Weitere Informationen hierzu finden Sie in Teil H von JSR 335.
Statische Methoden überschreiben
Die Antwort auf die zweite Frage ist etwas komplizierter.
Statische Methoden können zur Kompilierungszeit aufgelöst werden. Dynamischer Versand ist beispielsweise bei Methoden sinnvoll, bei denen der Compiler den konkreten Typ des Objekts nicht bestimmen und daher die aufzurufende Methode nicht auflösen kann. Das Aufrufen einer statischen Methode erfordert jedoch eine Klasse. Da diese Klasse zur Kompilierungszeit statisch bekannt ist , ist ein dynamischer Versand nicht erforderlich .
Ein kleiner Hintergrund zur Funktionsweise von Instanzmethoden ist erforderlich, um zu verstehen, was hier vor sich geht. Ich bin mir sicher, dass die tatsächliche Implementierung ganz anders ist, aber lassen Sie mich meinen Begriff des Methodenversands erläutern, der das beobachtete Verhalten genau modelliert.
Stellen Sie sich vor, jede Klasse verfügt über eine Hash-Tabelle, die Methodensignaturen (Name und Parametertypen) einem tatsächlichen Codeabschnitt zuordnet, um die Methode zu implementieren. Wenn die virtuelle Maschine versucht, eine Methode für eine Instanz aufzurufen, fragt sie das Objekt nach ihrer Klasse ab und sucht die angeforderte Signatur in der Tabelle der Klasse. Wenn ein Methodenkörper gefunden wird, wird er aufgerufen. Andernfalls wird die übergeordnete Klasse der Klasse abgerufen und die Suche dort wiederholt. Dies wird fortgesetzt, bis die Methode gefunden wurde oder keine übergeordneten Klassen mehr vorhanden sind. Dies führt zu a NoSuchMethodError
.
Wenn sowohl eine Oberklasse als auch eine Unterklasse einen Eintrag in ihren Tabellen für dieselbe Methodensignatur haben, wird zuerst die Version der Unterklasse gefunden und die Version der Oberklasse wird nie verwendet - dies ist eine "Überschreibung".
Angenommen, wir überspringen die Objektinstanz und beginnen einfach mit einer Unterklasse. Die Auflösung könnte wie oben beschrieben erfolgen und Ihnen eine Art "überschreibbare" statische Methode geben. Die Auflösung kann jedoch alle zur Kompilierungszeit erfolgen, da der Compiler von einer bekannten Klasse ausgeht und nicht bis zur Laufzeit wartet, um ein Objekt eines nicht angegebenen Typs nach seiner Klasse abzufragen. Es macht keinen Sinn, eine statische Methode zu "überschreiben", da man immer die Klasse angeben kann, die die gewünschte Version enthält.
Konstruktor "Schnittstellen"
Hier ist etwas mehr Material, um die letzte Bearbeitung der Frage zu behandeln.
Es hört sich so an, als ob Sie effektiv eine konstruktorähnliche Methode für jede Implementierung von festlegen möchten IXMLizable
. Vergessen Sie für eine Minute, dies mit einer Schnittstelle durchzusetzen, und tun Sie so, als hätten Sie einige Klassen, die diese Anforderung erfüllen. Wie würden Sie es verwenden?
class Foo implements IXMLizable<Foo> {
public static Foo newInstanceFromXML(Element e) { ... }
}
Foo obj = Foo.newInstanceFromXML(e);
Da Sie Foo
beim "Erstellen" des neuen Objekts den konkreten Typ explizit benennen müssen , kann der Compiler überprüfen, ob er tatsächlich über die erforderliche Factory-Methode verfügt. Und wenn nicht, was dann? Wenn ich eine implementieren kann IXMLizable
, der der "Konstruktor" fehlt, und ich eine Instanz erstelle und sie an Ihren Code übergebe, ist sie eine IXMLizable
mit allen erforderlichen Schnittstellen.
Die Konstruktion ist Teil der Implementierung, nicht der Schnittstelle. Jeder Code, der erfolgreich mit der Schnittstelle arbeitet, kümmert sich nicht um den Konstruktor. Jeder Code, der sich um den Konstruktor kümmert, muss den konkreten Typ sowieso kennen, und die Schnittstelle kann ignoriert werden.