CodeGym/Java blog/Véletlen/Java polimorfizmus
John Squirrels
Szint
San Francisco

Java polimorfizmus

Megjelent a csoportban
Az OOP-val kapcsolatos kérdések szerves részét képezik egy IT-cégnél egy Java fejlesztői pozícióra vonatkozó műszaki interjúnak. Ebben a cikkben az OOP egyik alapelvéről – a polimorfizmusról – fogunk beszélni. Azokra a szempontokra koncentrálunk, amelyekről gyakran kérdeznek az interjúk során, és néhány példát is adunk az érthetőség kedvéért.

Mi a polimorfizmus a Java nyelven?

A polimorfizmus egy program azon képessége, hogy azonos interfésszel rendelkező objektumokat ugyanúgy kezeljen, az objektum konkrét típusára vonatkozó információ nélkül. Ha válaszol egy kérdésre, hogy mi a polimorfizmus, valószínűleg meg kell magyaráznia, mire gondol. Anélkül, hogy további kérdéseket tenne fel, tárja fel mindezt még egyszer a kérdező számára. Interjú ideje: polimorfizmus Java nyelven - 1Kezdhetjük azzal a ténnyel, hogy az OOP megközelítés magában foglalja a Java program felépítését az objektumok közötti interakció alapján, amelyek osztályokon alapulnak. Az osztályok korábban megírt tervrajzok (sablonok), amelyeket objektumok létrehozására használnak a programban. Sőt, egy osztálynak mindig van egy konkrét típusa, aminek jó programozási stílus esetén a céljára utaló neve is van. Továbbá megjegyezhető, hogy mivel a Java erősen típusos, a programkódnak mindig meg kell adnia egy objektumtípust a változók deklarálásakor. Tegyük ehhez hozzá, hogy a szigorú gépelés javítja a kód biztonságát és megbízhatóságát, és már a fordításkor is lehetővé teszi az inkompatibilitási típusok miatti hibák megelőzését (például egy karakterlánc számmal való elosztása). Természetesen a fordítónak "tudnia" kell a deklarált típus – lehet egy osztály a JDK-ból, vagy egy, amit magunk hoztunk létre. Mutassa fel a kérdezőt, hogy kódunk nem csak a nyilatkozatban megjelölt típusú objektumokat használhatja, hanem azok leszármazottait is.Ez egy fontos szempont: sokféle típussal dolgozhatunk egyetlen típusként (feltéve, hogy ezek a típusok egy alaptípusból származnak). Ez azt is jelenti, hogy ha egy olyan változót deklarálunk, amelynek típusa szuperosztály, akkor az egyik leszármazottjának egy példányát hozzárendelhetjük ehhez a változóhoz. A kérdezőnek tetszeni fog, ha mondasz egy példát. Válasszon ki néhány osztályt, amelyet több osztály is megoszthat (egy alaposztályt), és hagyjon örökölni néhányat közülük. Alap osztály:
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() + " I dance like everyone else.");
    }

    @Override
    public String toString() {
        Return "I'm " + name + ". I'm " + age + " years old.";
    }
}
Az alosztályokban írja felül az alaposztály metódusát:
public class ElectricBoogieDancer extends Dancer {
    public ElectricBoogieDancer(String name, int age) {
        super(name, age);
    }
// Override the method of the base class
    @Override
    public void dance() {
        System.out.println(toString () + " I dance the electric boogie!");
    }
}

public class Breakdancer extends Dancer {

    public Breakdancer(String name, int age) {
        super(name, age);
    }
// Override the method of the base class
    @Override
    public void dance() {
        System.out.println(toString() + " I breakdance!");
    }
}
Példa a polimorfizmusra és arra, hogy ezek az objektumok hogyan használhatók fel egy programban:
public class Main {

    public static void main(String[] args) {
        Dancer dancer = new Dancer("Fred", 18);

        Dancer breakdancer = new Breakdancer("Jay", 19); // Widening conversion to the base type
        Dancer electricBoogieDancer = new ElectricBoogieDancer("Marcia", 20); // Widening conversion to the base type

        List<dancer> disco = Arrays.asList(dancer, breakdancer, electricBoogieDancer);
        for (Dancer d : disco) {
            d.dance(); // Call the polymorphic method
        }
    }
}
A módszerben mutasd meg, hogy a vonalak
Dancer breakdancer = new Breakdancer("Jay", 19);
Dancer electricBoogieDancer = new ElectricBoogieDancer("Marcia", 20);
deklaráljunk egy szuperosztály változóját, és rendeljünk hozzá egy objektumot, amely az egyik leszármazottjának példánya. Valószínűleg meg fogják kérdezni, hogy a fordító miért nem dől ki a hozzárendelési operátor bal és jobb oldalán deklarált típusok inkonzisztenciáján – elvégre a Java erősen begépelt. Magyarázza el, hogy itt kiszélesedő típuskonverzió működik – az objektumra való hivatkozást az alaposztályra való hivatkozásként kezeljük. Sőt, miután találkozott egy ilyen konstrukcióval a kódban, a fordító automatikusan és implicit módon végrehajtja az átalakítást. A mintakód azt mutatja, hogy a hozzárendelési operátor bal oldalán deklarált típusnak ( Dancer ) több formája (típusa) van, amelyek a jobb oldalon vannak deklarálva ( Breakdancer , ElectricBoogieDancer). Mindegyik formának megvan a maga egyedi viselkedése a szuperosztályban ( táncmódszer ) meghatározott általános funkcionalitás tekintetében . Vagyis egy szuperosztályban deklarált metódus a leszármazottaiban eltérően implementálható. Ebben az esetben metódus felülbírálással van dolgunk, ami pontosan több formát (viselkedést) hoz létre. Ez látható a kód futtatásával a fő metódusban: Program kimenet: Fred vagyok. 18 éves vagyok. Táncolok, mint mindenki más. Jay vagyok. Én 19 éves vagyok. én breaktáncot! Marcia vagyok. 20 éves vagyok. Táncolom az elektromos boogie-t! Ha nem írjuk felül a metódust az alosztályokban, akkor nem kapunk eltérő viselkedést. Például,ElectricBoogieDancer órákon, akkor a program kimenete ez lesz: Fred vagyok. 18 éves vagyok. Táncolok, mint mindenki más. Jay vagyok. Én 19 éves vagyok. Táncolok, mint mindenki más. Marcia vagyok. 20 éves vagyok. Táncolok, mint mindenki más. Ez pedig azt jelenti, hogy egyszerűen nincs értelme a Breakdancer és az ElectricBoogieDancer osztályok létrehozásának. Hol nyilvánul meg konkrétan a polimorfizmus elve? Hol használnak egy objektumot a programban a konkrét típusának ismerete nélkül? Példánkban ez akkor történik, amikor a dance() metódus meghívásra kerül a Dancer d objektumon. Java nyelven a polimorfizmus azt jelenti, hogy a programnak nem kell tudnia, hogy az objektum aBreakdancer vagy ElectricBoogieDancer . Az a fontos, hogy a táncos osztály leszármazottja . És ha a leszármazottakat említi, meg kell jegyezni, hogy a Java-ban az öröklődés nem csak kiterjesztések , hanem megvalósítások is.. Itt az ideje megemlíteni, hogy a Java nem támogatja a többszörös öröklődést – minden típusnak lehet egy szülője (szuperosztály) és korlátlan számú leszármazottja (alosztálya). Ennek megfelelően az interfészek több funkciókészlet hozzáadására szolgálnak az osztályokhoz. Az alosztályokhoz képest (öröklődés) az interfészek kevésbé kapcsolódnak a szülőosztályhoz. Nagyon széles körben használják. A Java nyelvben az interfész referenciatípus, így a program deklarálhat egy interfész típusú változót. Most itt az ideje, hogy példát mondjunk. Hozzon létre egy felületet:
public interface CanSwim {
    void swim();
}
Az egyértelműség kedvéért különféle nem kapcsolódó osztályokat veszünk, és megvalósítjuk az interfészt:
public class Human implements CanSwim {
    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()+" I swim with an inflated tube.");
    }

    @Override
    public String toString() {
        return "I'm " + name + ". I'm " + age + " years old.";
    }

}

public class Fish implements CanSwim {
    private String name;

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

    @Override
    public void swim() {
        System.out.println("I'm a fish. My name is " + name + ". I swim by moving my fins.");

    }

public class UBoat implements CanSwim {

    private int speed;

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

    @Override
    public void swim() {
        System.out.println("I'm a submarine that swims through the water by rotating screw propellers. My speed is " + speed + " knots.");
    }
}
módszer:
public class Main {

    public static void main(String[] args) {
        CanSwim human = new Human("John", 6);
        CanSwim fish = new Fish("Whale");
        CanSwim boat = new UBoat(25);

        List<swim> swimmers = Arrays.asList(human, fish, boat);
        for (Swim s : swimmers) {
            s.swim();
        }
    }
}
Az interfészben definiált polimorf metódust hívó eredmények megmutatják az interfészt megvalósító típusok viselkedésbeli különbségeit. Esetünkben ezek az úszásmódszer által megjelenített különböző húrok . Példánk tanulmányozása után a kérdező megkérdezheti, hogy miért fut ez a kód a módszerben
for (Swim s : swimmers) {
            s.swim();
}
az alosztályainkban definiált felülbíráló metódusok meghívását okozza? Hogyan történik a metódus kívánt megvalósításának kiválasztása a program futása közben? E kérdések megválaszolásához meg kell magyaráznia a késői (dinamikus) kötést. A kötés azt jelenti, hogy leképezést hozunk létre egy metódushívás és az adott osztálymegvalósítás között. Lényegében a kód határozza meg, hogy az osztályokban meghatározott három metódus közül melyik kerül végrehajtásra. A Java alapértelmezés szerint késői kötést használ, azaz a kötés futási időben történik, és nem fordítási időben, mint a korai kötés esetében. Ez azt jelenti, hogy amikor a fordító lefordítja ezt a kódot
for (Swim s : swimmers) {
            s.swim();
}
nem tudja, hogy melyik osztály ( Ember , Fish vagy Uboat ) rendelkezik az úszáskor végrehajtandó kóddalmódszert hívják. Ez a dinamikus kötési mechanizmusnak köszönhetően (az objektum típusának ellenőrzése futás közben és a megfelelő megvalósítás kiválasztása ehhez a típushoz) csak a program végrehajtásakor kerül meghatározásra. Ha megkérdezik, hogy ez hogyan valósul meg, akkor azt válaszolhatja, hogy az objektumok betöltésekor és inicializálása során a JVM táblákat épít fel a memóriában, és összekapcsolja a változókat az értékükkel, az objektumokat pedig a metódusaival. Ennek során, ha egy osztály öröklődik, vagy interfészt valósít meg, az első feladat az, hogy ellenőrizze a felülírt metódusok jelenlétét. Ha van ilyen, akkor ehhez a típushoz kötődnek. Ha nem, akkor az egyező metódus keresése az egy lépéssel magasabb osztályba (a szülőbe) kerül, és így tovább egészen a gyökérig egy többszintű hierarchiában. Ha az OOP polimorfizmusáról és kódban való megvalósításáról van szó, megjegyezzük, hogy jó gyakorlat absztrakt osztályok és interfészek használata az alaposztályok absztrakt definícióinak biztosítására. Ez a gyakorlat az absztrakció elvéből következik – a közös viselkedés és tulajdonságok azonosítása és egy absztrakt osztályba sorolása, vagy csak a közös viselkedés azonosítása és interfészbe helyezése. A polimorfizmus megvalósításához interfészeken és osztályöröklődésen alapuló objektumhierarchia tervezése és létrehozása szükséges. A Java polimorfizmusával és újításaival kapcsolatban megjegyezzük, hogy a Java 8-tól kezdve absztrakt osztályok és interfészek létrehozásakor lehetőség van a vagy csak a gyakori viselkedés azonosítása és interfészbe helyezése. A polimorfizmus megvalósításához interfészeken és osztályöröklődésen alapuló objektumhierarchia tervezése és létrehozása szükséges. A Java polimorfizmusával és újításaival kapcsolatban megjegyezzük, hogy a Java 8-tól kezdve absztrakt osztályok és interfészek létrehozásakor lehetőség van a vagy csak a gyakori viselkedés azonosítása és interfészbe helyezése. A polimorfizmus megvalósításához interfészeken és osztályöröklődésen alapuló objektumhierarchia tervezése és létrehozása szükséges. A Java polimorfizmusával és újításaival kapcsolatban megjegyezzük, hogy a Java 8-tól kezdve absztrakt osztályok és interfészek létrehozásakor lehetőség van aalapértelmezett kulcsszó az absztrakt metódusok alapértelmezett megvalósításának írásához az alaposztályokban. Például:
public interface CanSwim {
    default void swim() {
        System.out.println("I just swim");
    }
}
Néha a kérdezők megkérdezik, hogyan kell deklarálni az alaposztályokban lévő módszereket, hogy a polimorfizmus elve ne sérüljön. A válasz egyszerű: ezek a módszerek nem lehetnek statikusak , privátak vagy véglegesek . A Private csak egy osztályon belül tesz elérhetővé egy metódust, így nem tudja felülbírálni egy alosztályban. A Static egy metódust társít az osztályhoz, nem pedig objektumhoz, így a szuperosztály metódusa mindig meghívásra kerül. A final pedig megváltoztathatatlanná és az alosztályok elől rejtett metódust tesz.

Mit ad nekünk a polimorfizmus?

Valószínűleg arról is megkérdezik majd, hogy a polimorfizmus milyen előnyökkel jár számunkra. Erre röviden válaszolhat anélkül, hogy belemerülne a szőrös részletekbe:
  1. Lehetővé teszi az osztálymegvalósítások cseréjét. A tesztelés erre épül.
  2. Megkönnyíti a bővíthetőséget, így sokkal könnyebbé válik egy olyan alap létrehozása, amelyre a jövőben építeni lehet. Az OOP programok funkcionalitásának bővítésének legáltalánosabb módja a meglévők alapján új típusok hozzáadása.
  3. Lehetővé teszi, hogy a közös típusú vagy viselkedésű objektumokat egyetlen gyűjteménybe vagy tömbbe egyesítsd, és egységesen kezeld őket (mint a példánkban, ahol mindenkit táncra () vagy úszásra () kényszerítünk :)
  4. Rugalmasság új típusok létrehozásában: választhat egy metódus szülői implementációja mellett, vagy felülírhatja azt egy alosztályban.

Néhány búcsúzó szó

A polimorfizmus nagyon fontos és kiterjedt téma. A Java OOP-ról szóló cikk majdnem felének ez a témája, és a nyelv alapjainak jó részét képezi. Nem fogja tudni elkerülni, hogy ezt az elvet egy interjúban meghatározza. Ha nem tudod, vagy nem érted, valószínűleg az interjú véget ér. Tehát ne légy rest – az interjú előtt mérje fel tudását, és szükség esetén frissítse.

További olvasnivalók:

Hozzászólások
  • Népszerű
  • Új
  • Régi
Hozzászólás írásához be kell jelentkeznie
Ennek az oldalnak még nincsenek megjegyzései