Ich verstehe, dass statische Methoden in Java genau wie Instanzmethoden vererbt werden, mit dem Unterschied, dass bei der erneuten Deklaration die übergeordneten Implementierungen verborgen und nicht überschrieben werden. Gut, das macht Sinn. das Java-Tutorial merkt das jedoch
Statische Methoden in Schnittstellen werden niemals geerbt.
Warum? Was ist der Unterschied zwischen regulären und statischen Schnittstellenmethoden?
Lassen Sie mich klarstellen, was ich meine, wenn ich sage, dass statische Methoden vererbt werden können:
class Animal {
public static void identify() {
System.out.println("This is an animal");
}
}
class Cat extends Animal {}
public static void main(String[] args) {
Animal.identify();
Cat.identify(); // This compiles, even though it is not redefined in Cat.
}
Jedoch,
interface Animal {
public static void identify() {
System.out.println("This is an animal");
}
}
class Cat implements Animal {}
public static void main(String[] args) {
Animal.identify();
Cat.identify(); // This does not compile, because interface static methods do not inherit. (Why?)
}
Hier ist meine Vermutung.
Da Cat
nur eine Klasse erweitern kann, wenn Cat
Animal
verlängert, hat Cat.identify
nur eine Bedeutung. Cat
kann implementieren mehrere Schnittstellen, von denen jede eine statische Implementierung haben kann. Der Compiler würde also nicht wissen, welchen er wählen soll?
Wie der Autor jedoch darauf hinweist,
Java hat dieses Problem bereits mit Standardmethoden. Wenn zwei Schnittstellen default void identifizieren () deklarieren, welches verwendet wird? Es ist eine Kompilierung Fehler, und Sie müssen eine überschreibende Methode implementieren (die könnte nur Animal.super.identify () sein). So löst Java dies bereits Problem bei Standardmethoden - warum nicht bei statischen Methoden?
Wenn ich noch einmal raten sollte, würde ich sagen, dass die Implementierung mit default
Bestandteil der vtable von Cat
ist. Mit static
kann es nicht sein. Die Hauptfunktion muss an etwas binden. Zur Kompilierzeit konnte Cat.identify
vom Compiler durch Animal.identify
ersetzt werden, der Code würde jedoch nicht mit der Realität übereinstimmen, wenn Cat
erneut kompiliert würde, nicht jedoch die Klasse, die main enthält.
Vor Java 8 konnten Sie static
-Methoden nicht in einer interface
definieren. In dieser Frage wird heftig diskutiert. Ich werde auf dieses answer (von Benutzer @ JamesA.Rosen) verweisen, warum die Java-Designer anfangs wahrscheinlich keine static
-Methoden in einer interface
wollten:
Hier gibt es ein paar Probleme. Der erste ist die Ausgabe von Eine statische Methode deklarieren, ohne sie zu definieren. Das ist der Unterschied zwischen
public interface Foo {
public static int bar();
}
und
public interface Foo {
public static int bar() {
...
}
}
Java erlaubt es auch nicht, aber es könnte das zweite erlauben. Das erste ist unmöglich aus den Gründen, die Espo erwähnt: Sie wissen nicht, welche Klasse implementieren ist die richtige Definition.
Java könnte Letzteres zulassen, solange es Schnittstellen als .__ behandelt. erstklassige Objekte. Ruby's Module sind ungefähr Äquivalent zu Javas Interfaces lassen Sie genau das
module Foo
def self.bar
...
end
end
Seit der Veröffentlichung von Java 8 können Sie jedoch default
- und static
-Methoden innerhalb einer interface
hinzufügen.
Ich werde dieses source hier viel zitieren. Das ist das anfängliche Problem:
Mit der Oberflächensprachefunktion von Java können Sie Schnittstellen mit .__ deklarieren. abstrakte Methoden und Implementierungen dieser Methoden in der Klassen, die die Schnittstellen implementieren. Sie müssen .__ implementieren. jede Methode, die aufwändig ist, wenn .__ viele Methoden zur Verfügung stehen. implementieren. Nach dem Veröffentlichen der Benutzeroberfläche können Sie auch kein neues .__ hinzufügen. abstrakte Methoden dazu, ohne Quell- und Binärcode zu unterbrechen Kompatibilität.
Dies war die Lösung Java 8, die default
bereitgestellt wurde:
Java 8 behebt diese Probleme, indem es die Schnittstelle zur Unterstützung von .__ erweitert. Standard- und statische Methoden. Eine Standardmethode ist eine Instanzmethode in einer Schnittstelle definiert, deren Methodenheader mit der Standardeinstellung .__ beginnt. Stichwort; Es stellt auch einen Code-Body bereit. Jede Klasse, die das .__ implementiert. Die Schnittstelle erbt die Standardmethoden der Schnittstelle und kann .__ überschreiben. Sie
Und für static
:
Eine statische Methode ist eine Methode, die der Klasse zugeordnet ist, in der Es ist definiert und nicht mit einem Objekt, das aus dieser Klasse erstellt wurde. Jede Instanz der Klasse verwendet die statischen Methoden der Klasse . Mit Java 8 können auch statische Methoden in Schnittstellen definiert werden, an denen sie kann Standardmethoden unterstützen.
Wenn Sie eine Schnittstelle implementieren, die eine statische Methode enthält, wird die Die statische Methode ist immer noch Teil der Schnittstelle und nicht Teil der Klasse implementieren. Aus diesem Grund können Sie der Methode nicht mit .__ voranstellen. der Klassenname Stattdessen müssen Sie der Methode die Schnittstelle .__ voranstellen. Name
Beispiel:
interface X
{
static void foo()
{
System.out.println("foo");
}
}
class Y implements X
{
}
public class Z
{
public static void main(String[] args)
{
X.foo();
// Y.foo(); // won't compile
}
}
Der Ausdruck
Y.foo()
wird nicht kompiliert, dafoo()
ein statisches Member ist der SchnittstelleX
und kein statisches Member der KlasseY
.
Statische Methoden in Schnittstellen könnten einen Todesdiamanten erzeugen, wenn sie vererbt würden. Das Aufrufen einer statischen Methode von der entsprechenden Schnittstelle ist also gut genug im Vergleich zu dem Risiko, dass sie von einer konkreten Klasse aufgerufen wird, die möglicherweise mehrere Schnittstellen implementiert, die statische Methoden mit demselben Namen enthalten.
Warum unterscheiden sich statische Methoden?
Statische Methoden sind nur Funktionen, die sich nicht auf die Objekte beziehen. Anstatt sie in abstrakten Utility-Klassen (wie Aufruf von Collections.sort ()) zu platzieren, verschieben wir diese Funktionen (statische Methoden) an die entsprechenden Schnittstellen. Sie können an die geerbten Objekte gebunden sein, wie dies die Standardmethoden tun, aber das ist nicht ihre Aufgabe. Statische Methoden bieten Funktionen, die sich nicht auf die Instanzen der Klasse beziehen.
Beispiel:
interface Floatable {
default void float() {
// implementation
}
static boolean checkIfItCanFloat(Object fl) {
// some physics here
}
}
class Duck implements Floatable { }
Der Punkt ist also, dass eine Ente schwimmt, aber die Funktion, die prüft, ob ein Objekt wirklich schwimmt, kann eine Ente nicht tun. Es ist eine irrelevante Funktionalität, die wir an unsere Floatable-Schnittstelle übergeben könnten, anstatt sie in einer Utility-Klasse zu haben.
Beginnen wir mit etwas Hintergrundwissen ...
Java unterstützt keine Mehrfachvererbung (die Möglichkeit, mehr als eine Klasse zu erweitern). Dies ist darauf zurückzuführen, dass die Mehrfachvererbung zu einem tödlichen Diamanten des Todes (auch als Diamantproblem bekannt) neigt, den die Java-Designer vorhergesagt hatten.
Wenn B und C eine von A übernommene Methode überschreiben, welche Methode erbt D?
Eine Klasse kann mehrere Schnittstellen implementieren, da Schnittstellenmethoden zum Überschreiben vertraglich festgelegt werden. Wenn eine Klasse C zwei Schnittstellen A und B implementiert, die dieselbe Methode deklarieren, wird dieselbe Methode in C von Clients beider Schnittstellen (A oder B) aufgerufen. Die Einführung von Standardmethoden für Schnittstellen in Java 8 wurde ermöglicht, indem der Implementierer gezwungen wurde, den Standardwert bei Unklarheiten zu überschreiben. Dies war ein akzeptabler Kompromiss, da die Standardmethoden defensive sein sollen (zu verwenden, wenn keine andere Implementierung explizit von einem Implementierer bereitgestellt wird). Da der Compiler Sie jedoch nicht zwingen kann, eine statische Methode zu überschreiben (statische Methoden können an sich nicht überschrieben werden), ist die Einführung statischer Methoden für Schnittstellen in Java mit einer Einschränkung verbunden: Die statischen Methoden einer Schnittstelle sind dies nicht vererbt.