JavaRush /Java-Blog /Random-DE /Polymorphismus in Java

Polymorphismus in Java

Veröffentlicht in der Gruppe Random-DE
Fragen zu OOP sind ein wesentlicher Bestandteil eines technischen Vorstellungsgesprächs für die Stelle eines Java-Entwicklers in einem IT-Unternehmen. In diesem Artikel werden wir über eines der Prinzipien von OOP sprechen – den Polymorphismus. Wir konzentrieren uns auf Aspekte, die in Vorstellungsgesprächen häufig gefragt werden, und geben zur Verdeutlichung auch kleine Beispiele.

Was ist Polymorphismus?

Polymorphismus ist die Fähigkeit eines Programms, Objekte mit derselben Schnittstelle identisch zu verwenden, ohne Informationen über den spezifischen Typ dieses Objekts zu benötigen. Wenn Sie die Frage, was Polymorphismus ist, auf diese Weise beantworten, werden Sie höchstwahrscheinlich gebeten, zu erklären, was Sie meinen. Ordnen Sie noch einmal alles für den Interviewer, ohne viele zusätzliche Fragen zu stellen.

Polymorphismus in Java bei einem Interview - 1
Wir können mit der Tatsache beginnen, dass der OOP-Ansatz die Erstellung eines Java-Programms beinhaltet, das auf der Interaktion von Objekten basiert, die auf Klassen basieren. Klassen sind vorgefertigte Zeichnungen (Vorlagen), nach denen Objekte im Programm erstellt werden. Darüber hinaus hat eine Klasse immer einen bestimmten Typ, der bei einem guten Programmierstil seinen Zweck durch seinen Namen „verrät“. Darüber hinaus ist zu beachten, dass der Programmcode bei der Deklaration von Variablen immer den Typ des Objekts angeben muss, da Java eine stark typisierte Sprache ist. Hinzu kommt, dass eine strikte Typisierung die Codesicherheit und Programmzuverlässigkeit erhöht und es Ihnen ermöglicht, Typinkompatibilitätsfehler (z. B. den Versuch, eine Zeichenfolge durch eine Zahl zu dividieren) in der Kompilierungsphase zu verhindern. Natürlich muss der Compiler den deklarierten Typ „kennen“ – das kann eine Klasse aus dem JDK sein oder eine, die wir selbst erstellt haben. Bitte beachten Sie für den Interviewer, dass wir bei der Arbeit mit Programmcode nicht nur Objekte des Typs verwenden können, den wir bei der Deklaration zugewiesen haben, sondern auch dessen Nachkommen. Dies ist ein wichtiger Punkt: Wir können viele Typen so behandeln, als wären sie nur einer (solange diese Typen von einem Basistyp abgeleitet sind). Das bedeutet auch, dass wir, nachdem wir eine Variable eines Oberklassentyps deklariert haben, ihr den Wert eines ihrer Nachkommen zuweisen können. Dem Interviewer wird es gefallen, wenn Sie ein Beispiel nennen. Wählen Sie ein Objekt aus, das einer Gruppe von Objekten gemeinsam (Basis) sein kann, und erben Sie einige Klassen davon. Basisklasse:
public class Dancer {
    private String name;
    private int age;

    public Dancer(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void dance() {
        System.out.println(toString() + „Ich tanze wie alle anderen.“);
    }

    @Override
    public String toString() {
        return "Я " + name + ", mir " + age + " Jahre. " ;
    }
}
Überschreiben Sie in den Nachkommen die Basisklassenmethode:
public class ElectricBoogieDancer extends Dancer {
    public ElectricBoogieDancer(String name, int age) {
        super(name, age);
    }
// Überschreiben der Basisklassenmethode
    @Override
    public void dance() {
        System.out.println( toString() + „Ich tanze elektrischen Boogie!“);
    }
}

public class BreakDankDancer extends Dancer{

    public BreakDankDancer(String name, int age) {
        super(name, age);
    }
// Überschreiben der Basisklassenmethode
    @Override
    public void dance(){
        System.out.println(toString() + „Ich mache Breakdance!“);
    }
}
Ein Beispiel für Polymorphismus in Java und die Verwendung von Objekten in einem Programm:
public class Main {

    public static void main(String[] args) {
        Dancer dancer = new Dancer(„Anton“, 18);

        Dancer breakDanceDancer = new BreakDankDancer(„Alexei“, 19);// Upcast zum Basistyp
        Dancer electricBoogieDancer = new ElectricBoogieDancer(„Igor“, 20); // Upcast zum Basistyp

        List<Dancer> discotheque = Arrays.asList(dancer, breakDanceDancer, electricBoogieDancer);
        for (Dancer d : discotheque) {
            d.dance();// polymorpher Methodenaufruf
        }
    }
}
mainZeigen Sie im Methodencode an, was in den Zeilen steht:
Dancer breakDanceDancer = new BreakDankDancer(„Alexei“, 19);
Dancer electricBoogieDancer = new ElectricBoogieDancer(„Igor“, 20);
Wir haben eine Variable vom Typ Superklasse deklariert und ihr den Wert eines der Nachkommen zugewiesen. Höchstwahrscheinlich werden Sie gefragt, warum sich der Compiler nicht über die Nichtübereinstimmung zwischen den links und rechts vom Zuweisungszeichen deklarierten Typen beschwert, da Java über eine strikte Typisierung verfügt. Erklären Sie, dass hier die Aufwärtstypkonvertierung funktioniert – ein Verweis auf ein Objekt wird als Verweis auf die Basisklasse interpretiert. Darüber hinaus führt der Compiler dies automatisch und implizit aus, wenn er im Code auf eine solche Konstruktion stößt. Anhand des Beispielcodes kann gezeigt werden, dass der links vom Zuweisungszeichen deklarierte Klassentyp Dancermehrere Formen (Typen) hat, die rechts deklariert sind BreakDankDancer, ElectricBoogieDancer. Jedes der Formulare kann sein eigenes einzigartiges Verhalten für allgemeine Funktionen haben, die in der Superklassenmethode definiert sind dance. Das heißt, eine in einer Oberklasse deklarierte Methode kann in ihren Nachkommen unterschiedlich implementiert werden. In diesem Fall handelt es sich um das Überschreiben von Methoden, und genau dadurch entstehen vielfältige Formen (Verhaltensweisen). Sie können dies sehen, indem Sie den Hauptmethodencode zur Ausführung ausführen: Programmausgabe Ich bin Anton, ich bin 18 Jahre alt. Ich tanze wie alle anderen. Ich bin Alexey, ich bin 19 Jahre alt. Ich tanze Breakdance! Ich bin Igor, ich bin 20 Jahre alt. Ich tanze den elektrischen Boogie! Wenn wir in den Nachkommen kein Überschreiben verwenden, erhalten wir kein anderes Verhalten. BreakDankDancerWenn wir beispielsweise die Methode für unsere Klassen ElectricBoogieDancerauskommentieren dance, sieht die Ausgabe des Programms so aus: Ich bin Anton, ich bin 18 Jahre alt. Ich tanze wie alle anderen. Ich bin Alexey, ich bin 19 Jahre alt. Ich tanze wie alle anderen. Ich bin Igor, ich bin 20 Jahre alt. Ich tanze wie alle anderen. und das bedeutet, dass es einfach keinen Sinn macht, neue BreakDankDancerKlassen zu erstellen ElectricBoogieDancer. Was genau ist das Prinzip des Java-Polymorphismus? Wo ist es versteckt, ein Objekt in einem Programm zu verwenden, ohne seinen spezifischen Typ zu kennen? In unserem Beispiel handelt es sich um einen Methodenaufruf d.dance()für ein Objekt dvom Typ Dancer. Java-Polymorphismus bedeutet, dass das Programm nicht wissen muss, welchen Typ das Objekt BreakDankDanceroder Objekt haben wird ElectricBoogieDancer. Die Hauptsache ist, dass es ein Nachkomme der Klasse ist Dancer. Und wenn wir über Nachkommen sprechen, sollte beachtet werden, dass die Vererbung in Java nicht nur extends, sondern auch ist implements. Jetzt ist es an der Zeit, sich daran zu erinnern, dass Java keine Mehrfachvererbung unterstützt – jeder Typ kann ein übergeordnetes Element (Superklasse) und eine unbegrenzte Anzahl von Nachkommen (Unterklassen) haben. Daher werden Schnittstellen verwendet, um Klassen mehrere Funktionen hinzuzufügen. Schnittstellen verringern im Vergleich zur Vererbung die Kopplung von Objekten an ein übergeordnetes Element und werden sehr häufig verwendet. In Java ist eine Schnittstelle ein Referenztyp, sodass ein Programm den Typ als Variable des Schnittstellentyps deklarieren kann. Dies ist ein guter Zeitpunkt, um ein Beispiel zu nennen. Lassen Sie uns die Schnittstelle erstellen:
public interface Swim {
    void swim();
}
Nehmen wir der Übersichtlichkeit halber verschiedene und voneinander unabhängige Objekte und implementieren eine Schnittstelle in ihnen:
public class Human implements Swim {
    private String name;
    private int age;

    public Human(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public void swim() {
        System.out.println(toString()+„Ich schwimme mit einem aufblasbaren Ring.“);
    }

    @Override
    public String toString() {
        return "Я " + name + ", mir " + age + " Jahre. ";
    }

}

public class Fish implements Swim{
    private String name;

    public Fish(String name) {
        this.name = name;
    }

    @Override
    public void swim() {
        System.out.println("Ich bin ein Fisch " + name + „Ich schwimme, indem ich meine Flossen bewege.“);

    }

public class UBoat implements Swim {

    private int speed;

    public UBoat(int speed) {
        this.speed = speed;
    }

    @Override
    public void swim() {
        System.out.println(„Das U-Boot segelt und dreht die Propeller mit einer Geschwindigkeit.“ + speed + „Knoten.“);
    }
}
Methode main:
public class Main {

    public static void main(String[] args) {
        Swim human = new Human(„Anton“, 6);
        Swim fish = new Fish("Wal");
        Swim boat = new UBoat(25);

        List<Swim> swimmers = Arrays.asList(human, fish, boat);
        for (Swim s : swimmers) {
            s.swim();
        }
    }
}
Das Ergebnis der Ausführung einer in einer Schnittstelle definierten polymorphen Methode ermöglicht es uns, Unterschiede im Verhalten der Typen zu erkennen, die diese Schnittstelle implementieren. Sie bestehen aus unterschiedlichen Ergebnissen der Methodenausführung swim. Nach dem Studium unseres Beispiels fragt sich der Interviewer möglicherweise, warum, wenn er den Code ausführtmain
for (Swim s : swimmers) {
            s.swim();
}
Werden die in diesen Klassen definierten Methoden für unsere Objekte aufgerufen? Wie wählt man bei der Ausführung eines Programms die gewünschte Implementierung einer Methode aus? Um diese Fragen zu beantworten, müssen wir über späte (dynamische) Bindung sprechen. Unter Bindung verstehen wir die Herstellung einer Verbindung zwischen einem Methodenaufruf und seiner spezifischen Implementierung in Klassen. Im Wesentlichen bestimmt der Code, welche der drei in den Klassen definierten Methoden ausgeführt wird. Java verwendet standardmäßig die späte Bindung (zur Laufzeit und nicht zur Kompilierzeit, wie es bei der frühen Bindung der Fall ist). Dies bedeutet, dass beim Kompilieren des Codes
for (Swim s : swimmers) {
            s.swim();
}
Der Compiler weiß noch nicht, aus welcher Klasse der Code stammt Humanund Fishob Uboater in der ausgeführt wird swim. Dies wird erst bei der Ausführung des Programms durch den Mechanismus des dynamischen Versands festgestellt, bei dem der Typ des Objekts während der Programmausführung überprüft und die gewünschte Implementierung der Methode für diesen Typ ausgewählt wird. Wenn Sie gefragt werden, wie dies implementiert wird, können Sie antworten, dass die JVM beim Laden und Initialisieren von Objekten Tabellen im Speicher erstellt und darin Variablen ihren Werten und Objekte ihren Methoden zuordnet. Darüber hinaus wird, wenn ein Objekt geerbt ist oder eine Schnittstelle implementiert, zunächst das Vorhandensein überschriebener Methoden in seiner Klasse überprüft. Wenn welche vorhanden sind, werden sie an diesen Typ gebunden. Wenn nicht, wird nach einer Methode gesucht, die in der Klasse eine Ebene höher (im übergeordneten Element) definiert ist, und so weiter bis zur Wurzel in einer mehrstufigen Hierarchie. Wenn wir über Polymorphismus in OOP und seine Implementierung im Programmcode sprechen, stellen wir fest, dass es eine gute Praxis ist, abstrakte Beschreibungen zu verwenden, um Basisklassen mithilfe abstrakter Klassen und Schnittstellen zu definieren. Diese Praxis basiert auf der Verwendung von Abstraktion – dem Isolieren allgemeiner Verhaltensweisen und Eigenschaften und dem Einschließen dieser in eine abstrakte Klasse oder der Isolierung nur des allgemeinen Verhaltens – in diesem Fall erstellen wir eine Schnittstelle. Der Aufbau und Entwurf einer Hierarchie von Objekten auf der Grundlage von Schnittstellen und Klassenvererbung ist eine Voraussetzung für die Erfüllung des Prinzips des OOP-Polymorphismus. Zum Thema Polymorphismus und Innovationen in Java können wir erwähnen, dass es beim Erstellen abstrakter Klassen und Schnittstellen ab Java 8 möglich ist, mit dem Schlüsselwort eine Standardimplementierung abstrakter Methoden in Basisklassen zu schreiben default. Zum Beispiel:
public interface Swim {
    default void swim() {
        System.out.println(„Einfach schweben“);
    }
}
Manchmal fragen sie nach den Anforderungen für die Deklaration von Methoden in Basisklassen, damit das Prinzip des Polymorphismus nicht verletzt wird. Hier ist alles einfach: Diese Methoden sollten nicht static , private und final sein . Private macht die Methode nur in der Klasse verfügbar und Sie können sie im Nachkommen nicht überschreiben. Statisch macht die Methode zur Eigenschaft der Klasse und nicht zum Objekt, sodass immer die Methode der Oberklasse aufgerufen wird. Final macht die Methode unveränderlich und vor ihren Erben verborgen.

Was bringt uns Polymorphismus in Java?

Es wird sich höchstwahrscheinlich auch die Frage stellen, was uns die Verwendung von Polymorphismus bringt. Hier können Sie kurz antworten, ohne zu tief ins Detail zu gehen:
  1. Ermöglicht das Ersetzen von Objektimplementierungen. Darauf basiert das Testen.
  2. Bietet Programmerweiterbarkeit – es wird viel einfacher, eine Grundlage für die Zukunft zu schaffen. Das Hinzufügen neuer Typen basierend auf vorhandenen Typen ist die häufigste Methode, um die Funktionalität von im OOP-Stil geschriebenen Programmen zu erweitern.
  3. Ermöglicht Ihnen, Objekte mit einem gemeinsamen Typ oder Verhalten in einer Sammlung oder einem Array zu kombinieren und sie einheitlich zu verwalten (wie in unseren Beispielen, alle zum Tanzen zu bringen – eine Methode danceoder schwimmen zu lassen – eine Methode swim).
  4. Flexibilität beim Erstellen neuer Typen: Sie können wählen, ob Sie eine Methode von einem übergeordneten Typ implementieren oder sie in einem untergeordneten Typ überschreiben möchten.

Abschiedsworte für die Reise

Das Prinzip des Polymorphismus ist ein sehr wichtiges und weitreichendes Thema. Es deckt fast die Hälfte von Javas OOP und einen guten Teil der Grundlagen der Sprache ab. Sie werden nicht umhinkommen, diesen Grundsatz in einem Vorstellungsgespräch zu definieren. Unwissenheit oder Missverständnisse darüber werden höchstwahrscheinlich das Ende des Interviews bedeuten. Scheuen Sie sich daher nicht, Ihre Kenntnisse vor der Prüfung zu überprüfen und bei Bedarf aufzufrischen.
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION