2 Stimmen

Vererbung und abstrakte Klassen in Java verstehen

In Ordnung, so habe ich für die Dokumentation durch Googeln gesucht, aber ich habe keine gefunden, die wirklich beschreibt, was ich suche, um zu beantworten, so hier bin ich fragen Sie Jungs.

Ich verstehe also die Vererbung und wie sie funktioniert. Das Problem, das ich habe, ist, dass ich manchmal ein Objekt sehe, das ursprünglich als ein Typ definiert war und dann auf einen anderen Typ gesetzt wird, und ich verstehe nicht genau, was da passiert. Hier ist ein Beispiel:

Sagen wir, ich habe eine Klasse Tier und die Klassen Katze und Hund, die Tier erweitern. Katze, Tier und Hund haben alle eine Methode speak(), die für Katze "miau" und für Hund "wuff" ausgibt und für Tier "kann nicht sprechen".

Nun gut, hier ist endlich meine Frage. Was genau passiert, wenn ich eine Katze (c) mache und dann Animal a = c; ausführe? Was passiert, wenn ich a.speak(); ausführe? Welche speak-Methode wird aufgerufen? Was genau ist passiert, wenn ich den Typ auf diese Weise ändere? Werde ich jemals einen echten Grund haben, dies zu benutzen?

Was die abstrakten Methoden angeht, so frage ich mich, wozu sie eigentlich gut sein sollen. In den Beispielen, die ich gesehen habe, wurden sie in Superklassen eingefügt, und die darunter liegenden Klassen legen das genaue Verhalten fest. Wenn man eine abstrakte Methode in eine Superklasse einfügt, müssen dann alle darunter liegenden Klassen sie implementieren?

Vielen Dank für Ihre Hilfe!

3voto

thejh Punkte 43512

Was genau passiert, wenn man eine Katze macht (c) und dann Animal a = c; ausführt? Was passiert, wenn ich a.speak(); ausführe? Welche speak-Methode wird aufgerufen? Was genau passiert, wenn ich die Typen so ändere das? Werde ich jemals einen echten Grund haben dies zu verwenden?

Immer die Methode der eigentlichen Klasse, zum Beispiel in diesem Fall die speak() Methode der Katze.

Was die abstrakten Methoden betrifft, so ist meine Frage: Was genau ist der Sinn sie zu haben?

Sie sorgen dafür, dass zum Beispiel jedes Tier eine Methode hat walk() die Sie bei jedem Tier aufrufen können. Es ist eine Garantie, die besagt, dass "jedes Animal Objekt diese Methode hat, müssen Sie sich nicht darum kümmern".

In den Beispielen, die ich wurden sie in Superklassen eingeordnet und die Klassen darunter definieren das genaue Verhalten. Indem man eine abstrakte Methode in einer Superklasse ist eine alle darunter liegenden Klassen dazu verpflichtet implementieren?

Um es umzusetzen oder auch um abstrakt zu sein, ja.

2voto

Kent Punkte 180747

Katze c = new Katze(); Tier a = c; a.speak() druckt miau.

bitte prüfen java polymorphismus aus.

über die abstrakte Klasse:

Wenn eine abstrakte Klasse subklassifiziert wird, bietet die Unterklasse normalerweise Implementierungen für alle abstrakten Methoden ihrer Elternklasse. Ist dies jedoch nicht der Fall, muss die Unterklasse ebenfalls als abstrakt deklariert werden.

In der JLS, Abschnitt 5.2 wird erklärt, warum Cat ist zuordenbar zu Animal . (Beachten Sie, dass Animal ist nicht implizit zuweisbar an Cat denn Cat ist eine "spezifischere" Art; Cat ist ein Subtyp von Animal et Animal ist der Supertyp von Cat )

Die Zuweisung eines Wertes vom Kompilierzeit-Referenztyp S (Quelle) zu einer Variablen vom Kompilierzeit-Referenztyp T (Ziel) wird wie folgt geprüft:

  • Wenn S eine Klassenart ist:
    • Wenn T ein Klassentyp ist, muss S entweder dieselbe Klasse wie T sein, oder S muss eine Unterklasse von T sein, oder es tritt ein Kompilierfehler auf.
    • Wenn T ein Schnittstellentyp ist, dann muss S die Schnittstelle T implementieren, oder es tritt ein Kompilierfehler auf.
    • Wenn T ein Array-Typ ist, tritt ein Kompilierfehler auf.
  • Wenn S ein Schnittstellentyp ist:
    • Wenn T ein Klassentyp ist, dann muss T Object oder es tritt ein Kompilierfehler auf.
    • Wenn T ein Schnittstellentyp ist, muss T entweder dieselbe Schnittstelle wie S oder eine Superschnittstelle von S sein, sonst tritt ein Kompilierfehler auf.
    • Wenn T ein Array-Typ ist, tritt ein Kompilierfehler auf.
  • Wenn S ein Array vom Typ SC[] ist, d. h. ein Array von Komponenten des Typs SC:

[der Kürze halber weggelassen]

1voto

Donal Fellows Punkte 125686

Sagen wir, ich habe eine Klasse Tier und die Klassen Katze und Hund, die Tier erweitern. Katze, Tier und Hund haben alle eine Methode speak(), die für Katze "miau" und für Hund "wuff" ausgibt und für Tier "kann nicht sprechen".

Nun gut, hier ist endlich meine Frage. Was genau passiert, wenn ich eine Katze ( c ) und führen Sie dann Animal a = c; ? Was passiert, wenn ich a.speak(); ? Welche speak() Methode aufgerufen wird? Was genau ist passiert, wenn ich die Typen auf diese Weise ändere? Werde ich jemals einen echten Grund haben, dies zu verwenden?

Objekte in Java wissen genau, als welcher Typ sie erstellt wurden; es ist quasi ein verstecktes Feld (das Sie mit der Funktion Object.getClass() Methode). Darüber hinaus beginnt die Auflösung aller nichtstatischen Methoden mit den Methodendefinitionen der spezifischsten Klasse und geht dann zur generischsten Klasse ( Object ); da es in Java immer nur eine einzige Vererbung der Implementierung gibt, ist dies eine einfache Suche. Cat weiß, dass es ein Subtyp von Animal , die ein Subtyp ist von Object und c weiß, dass es sich um eine Cat unabhängig vom Typ der Variablen.

Wenn Sie die Zuordnung vornehmen, wird die Compiler prüft, ob der bekannte Typ des zugewiesenen Wertes der Typ ist, der zugewiesen wird oder einer seiner Subtypen . Wenn ja, funktioniert die Zuweisung. Wenn nicht, brauchen Sie einen expliziten Cast (der zur Laufzeit eine korrekte Typüberprüfung vornimmt; Casts können das Typsystem von Java nicht zerstören, sie können es nur hässlich machen). Es ändert nichts an der Tatsache, dass die Methodensuche immer noch dynamisch erfolgt und das Objekt immer noch weiß, welcher Typ es wirklich ist; das Programm vernachlässigt lediglich einige Informationen. Wenn Sie C++ kennen, dann stellen Sie sich vor, dass Java nur virtuelle Methoden (und statische Methoden) hat, und es ist sehr einfach, die Lookups in den vtables zu handhaben, weil es kein Problem mit Vererbungsdiamanten oder anderen bösen Fällen gibt.

Bei der Arbeit mit Implementierungen einer Schnittstelle ist es fast dasselbe, außer dass ein komplexeres Lookup durchgeführt wird (d.h. zuerst wird der Index in der vtable nachgeschlagen, bevor man wie zuvor fortfährt). Ein Objekt zu haben, das die Schnittstelle implementiert, bedeutet jedoch, dass es eine Klasse geben muss, die die Schnittstelle vollständig implementiert, und dann ist alles wieder relativ einfach. Und denken Sie daran, dass alle wirklich komplizierten Dinge zur Kompilierungszeit erledigt werden; zur Laufzeit sind die Dinge in allen Fällen relativ einfach.

Werden Sie etwas davon in Anspruch nehmen? Nun, Sie sollte (und Sie werden feststellen, dass es sich in echtem Code nur schwer vermeiden lässt). Es ist ein guter Stil, sich einen Vertrag vorzustellen, der durch eine Schnittstelle oder Superklasse definiert ist, wobei die Subtypen den Vertrag befolgen, ohne dass der Aufrufer die Details kennen muss. Java-Bibliotheken verwenden dies sehr häufig, zumal die Sichtbarkeit der Typdetails des Vertrags und seine Erfüllung unterschiedlich sein können. Alles, was der Client-Code weiß, ist, dass das Objekt dem Vertrag gehorcht, dass es vom gegebenen Typ ist.

0voto

David Young Punkte 4493

Je nachdem, was Ihr Hintergrund ist, C++ oder Java, können die Dinge sehr verwirrend werden.

In C++ gibt es das Konzept der virtuellen Funktionen, die zur Laufzeit nachgeschlagen werden, um zu entscheiden, zu welcher Klasse die Funktion tatsächlich gehört. Es gibt auch nicht-virtuelle Funktionen, die auf der Grundlage des Variablentyps aufgerufen werden.

In Java sind jedoch alle Methoden grundsätzlich virtuell, was bedeutet, dass Java-Methoden immer zur Laufzeit nachgeschlagen werden, ein Prozess, der Laufzeit-Polymorphismus

Ein Vorteil ist etwa dieser

class Animal{
    public void type(){
        System.out.println("animal");

    }
}

class Dog extends Animal{
    public void type(){
        System.out.println("dog");
    }
}

class Cat extends Animal{
    public void type(){
        System.out.println("cat");
    }
}

public class Driver{
    public static void main(String[] args){
        Animal[] animals = new Animal[3];
        animals[0] = new Animal();
        animals[1] = new Dog();
        animals[2] = new Cat();

        for(Animal animal: animals){
            animal.type();
        }
    }
}

Dies führt zur Ausgabe

animal
dog
cat

0voto

jvdbogae Punkte 1249

So wie ich das verstehe, verwenden Sie Schnittstellen, um Ihren Code zu entkoppeln. Sie wollen gegen Schnittstellen programmieren, statt gegen Implementierungen: Was bedeutet es, "auf eine Schnittstelle zu programmieren"? . Sie würden eine abstrakte Klasse verwenden, um die Funktion zu implementieren, die für alle implementierenden Klassen trivial ist, so dass Sie sie nicht in alle implementierenden Klassen schreiben müssen.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X