web-dev-qa-db-de.com

Warum werden statische Klassenmethoden geerbt, aber keine statischen Schnittstellenmethoden?

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?)
}
42

Hier ist meine Vermutung. 

Da Cat nur eine Klasse erweitern kann, wenn CatAnimal 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.

17
agbinfo

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, da foo() ein statisches Member ist der Schnittstelle X und kein statisches Member der Klasse Y.

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.

3
Styl

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. 

 enter image description here

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.