Programiranje

Java polimorfizem in njegove vrste

Polimorfizem se nanaša na sposobnost nekaterih entitet, da se pojavljajo v različnih oblikah. V popularnosti ga predstavlja metulj, ki se preobrazi od ličinke do lutke do imaga. Polimorfizem obstaja tudi v programskih jezikih kot tehnika modeliranja, ki vam omogoča, da ustvarite en sam vmesnik za različne operande, argumente in predmete. Rezultat polimorfizma Java je koda, ki je bolj jedrnata in enostavnejša za vzdrževanje.

Čeprav se ta vadnica osredotoča na polimorfizem podtipov, morate vedeti še nekaj drugih. Začeli bomo s pregledom vseh štirih vrst polimorfizma.

prenos Prenesite kodo Prenesite izvorno kodo, na primer programe v tej vadnici. Ustvaril Jeff Friesen za JavaWorld.

Vrste polimorfizma v Javi

V Javi obstajajo štiri vrste polimorfizma:

  1. Prisila je operacija, ki služi implicitnim pretvorbam več vrst. Celo število na primer delite z drugim celim številom ali vrednost s plavajočo vejico z drugo vrednostjo s plavajočo vejico. Če je en operand celo število, drugi pa vrednost s plavajočo vejico, je prevajalnik prisile (implicitno pretvori) celo število v vrednost s plavajočo vejico, da se prepreči napaka tipa. (Nobena operacija delitve ne podpira celoštevilčnega operanda in operanda s plavajočo vejico.) Drug primer je posredovanje sklica na objekt podrazreda na parameter superklase metode. Prevajalnik prisili tip podrazreda na tip superrazreda, da omeji operacije na nadrazred.
  2. Preobremenitev se nanaša na uporabo istega operaterskega simbola ali imena metode v različnih kontekstih. Na primer, lahko uporabite + za izvedbo seštevanja celih števil, dodajanja s plavajočo vejico ali združevanja nizov, odvisno od vrst njegovih operandov. V razredu se lahko pojavi tudi več metod z istim imenom (z izjavo in / ali dedovanjem).
  3. Parametrično polimorfizem določa, da se lahko v izjavi razreda ime polja poveže z različnimi tipi, ime metode pa z različnimi vrstami parametrov in vrnitev. Nato lahko polje in metoda prevzameta različne tipe v vsakem primerku razreda (predmeta). Polje je na primer lahko vrste Dvojno (član standardne knjižnice razredov Java, ki zavije a dvojno vrednost) in metoda lahko vrne a Dvojno v enem predmetu, isto polje pa je lahko tipa Vrvica in ista metoda lahko vrne a Vrvica v drugem predmetu. Java s pomočjo generikov podpira parametrični polimorfizem, o čemer bom razpravljal v prihodnjem članku.
  4. Podtip pomeni, da lahko tip služi kot podtip drugega tipa. Ko se primerek podtipa pojavi v kontekstu nadtipa, izvajanje operacije nadtipa na primerku podtipa povzroči izvedbo različice podtipa te operacije. Na primer, razmislite o fragmentu kode, ki nariše poljubne oblike. To risbeno kodo lahko izrazite bolj jedrnato z uvedbo a Oblika razred z a žreb () metoda; z uvajanjem Krog, Pravokotnikin drugi podrazredi, ki preglasijo žreb (); z uvedbo polja tipa Oblika katerih elementi shranjujejo reference na Oblika primeri podrazreda; in s klicem Oblikaje žreb () za vsak primerek. Ko pokličete žreb (), to je Krogje, Pravokotnikali drugače Oblika primerki žreb () metoda, ki se pokliče. Pravimo, da obstaja veliko oblik Oblikaje žreb () metoda.

Ta vadnica predstavlja polimorfizem podtipa. Spoznali boste posodobitve in pozno vezavo, abstraktne razrede (ki jih ni mogoče instancirati) in abstraktne metode (ki jih ni mogoče poklicati). Spoznali boste tudi informacije o prenosu in identifikaciji tipa izvajalnega okolja ter si prvič ogledali kovariante vrst vrnitve. Parametrični polimorfizem bom shranil za prihodnjo vadnico.

Ad-hoc vs univerzalni polimorfizem

Kot mnoge razvijalce prisilo in preobremenitev uvrščam med ad hoc polimorfizem, parametrične in podtipe pa kot univerzalni polimorfizem. Čeprav sta dragoceni tehniki, ne verjamem, da sta prisila in preobremenitev resnični polimorfizem; so bolj podobni pretvorbi vrst in skladenjskemu sladkorju.

Podtip polimorfizma: posodobitev in pozna vezava

Podtip polimorfizma temelji na posodabljanju in pozni vezavi. Upcasting je oblika predvajanja, pri kateri razvrstite hierarhijo dedovanja iz podtipa v nadtip. Noben operater zasedbe ni vključen, ker je podtip specializacija nadtipa. Na primer, Oblika s = nov krog (); posodobitve iz Krog do Oblika. To je smiselno, ker je krog neke vrste oblika.

Po posodobitvi Krog do Oblika, ne morete poklicati Krog-specifične metode, kot je a getRadius () metoda, ki vrne polmer kroga, ker Krog-specifične metode niso del Oblikaje vmesnik. Izguba dostopa do funkcij podtipa po zožitvi podrazreda na njegov superrazred se zdi nesmiselna, vendar je nujna za doseganje polimorfizma podtipa.

Recimo, da Oblika izjavlja a žreb () metoda, njegova Krog podrazred preglasi to metodo, Oblika s = nov krog (); je pravkar izvedena, naslednja vrstica pa določa s. risanje ();. Kateri žreb () metoda se imenuje: Oblikaje žreb () metoda oz Krogje žreb () metoda? Prevajalnik ne ve, kateri žreb () način klica. Vse, kar lahko naredi, je preveriti, ali metoda obstaja v superklasi, in preveriti, ali se seznam argumentov in tip vrnitve klica metode ujema z izjavo metode superklase. Vendar pa prevajalnik v prevedeno kodo vstavi tudi navodilo, ki med izvajanjem pridobi in uporabi vse sklice, ki jih vsebuje s da pokličete pravilno žreb () metoda. Ta naloga je znana kot pozna vezava.

Pozna vezava vs zgodnja vezava

Pozna vezava se uporablja za klicedokončno primerov. Za vse druge klice metode prevajalnik ve, katero metodo naj pokliče. V prevedeno kodo vstavi navodilo, ki pokliče metodo, povezano s tipom spremenljivke, in ne z njeno vrednostjo. Ta tehnika je znana kot zgodnja vezava.

Ustvaril sem aplikacijo, ki prikazuje polimorfizem podtipa v smislu posodobitve in pozne vezave. Ta aplikacija je sestavljena iz Oblika, Krog, Pravokotnik, in Oblike razredi, kjer je vsak razred shranjen v svoji izvorni datoteki. Seznam 1 predstavlja prve tri razrede.

Seznam 1. Izjava o hierarhiji oblik

class Shape {void draw () {}} class Circle extends Shape {private int x, y, r; Krog (int x, int y, int r) {this.x = x; to.y = y; to.r = r; } // Zaradi kratkosti sem izpustil metode getX (), getY () in getRadius (). @Override void draw () {System.out.println ("Risalni krog (" + x + "," + y + "," + r + ")"); }} razred Rectangle razširja Shape {private int x, y, w, h; Pravokotnik (int x, int y, int w, int h) {this.x = x; to.y = y; to.w = w; to.h = h; } // Zaradi kratkosti sem izpustil metode getX (), getY (), getWidth () in getHeight () //. @Override void draw () {System.out.println ("Risalni pravokotnik (" + x + "," + y + "," + w + "," + h + ")"); }}

Seznam 2 predstavlja Oblike aplikacijski razred, katerega glavni () metoda poganja aplikacijo.

Seznam 2. Nadgrajevanje in pozna vezava pri polimorfizmu podtipa

class Shapes {public static void main (String [] args) {Shape [] shape = {new Circle (10, 20, 30), new Rectangle (20, 30, 40, 50)}; for (int i = 0; i <shape.length; i ++) oblik [i] .draw (); }}

Izjava oblike matrika prikazuje upcasting. The Krog in Pravokotnik sklici so shranjeni v oblike [0] in oblike [1] in so posodobljeni za tipkanje Oblika. Vsak od oblike [0] in oblike [1] se šteje za a Oblika primer: oblike [0] se ne šteje za Krog; oblike [1] se ne šteje za Pravokotnik.

Pozno vezavo dokazuje oblike [i] .draw (); izraz. Kdaj jaz enako 0, vzrok, ki ga ustvari prevajalnik Krogje žreb () metoda, ki jo je treba poklicati. Kdaj jaz enako 1, vendar to navodilo povzroča Pravokotnikje žreb () metoda, ki jo je treba poklicati. To je bistvo podtipa polimorfizma.

Ob predpostavki, da so vse štiri izvorne datoteke (Oblike.java, Oblika.java, Pravokotnik.java, in Krog.java) se nahajajo v trenutnem imeniku, jih prevedite v eno od naslednjih ukaznih vrstic:

javac * .java javac Shapes.java

Zaženite nastalo aplikacijo:

java Oblike

Upoštevati morate naslednje rezultate:

Risalni krog (10, 20, 30) Risalni pravokotnik (20, 30, 40, 50)

Abstraktni razredi in metode

Pri načrtovanju hierarhij razredov boste ugotovili, da so razredi bližje vrhu teh hierarhij bolj splošni kot razredi nižje. Na primer, a Vozilo superrazred je bolj splošen kot a Tovornjak podrazred. Podobno a Oblika superrazred je bolj splošen kot a Krog ali a Pravokotnik podrazred.

Primer generičnega razreda ni smiselno. Konec koncev, kaj bi a Vozilo objekt opisati? Podobno kakšno obliko predstavlja a Oblika predmet? Namesto da bi prazno kodo žreb () metoda v Oblika, lahko preprečimo klicanje te metode in instanciranje tega razreda z razglasitvijo obeh entitet za abstraktne.

Java ponuja povzetek rezervirana beseda za razglasitev razreda, ki ga ni mogoče ustvariti. Prevajalnik poroča o napaki, ko poskusite primeriti ta razred. povzetek se uporablja tudi za razglasitev metode brez telesa. The žreb () metoda ne potrebuje telesa, ker ne more narisati abstraktne oblike. Seznam 3 prikazuje.

Seznam 3. Povzemanje razreda Shape in metode draw ()

abstraktni razred Oblika {abstract void draw (); // podpičje je obvezno}

Povzetek opozorila

Prevajalnik poroča o napaki, ko poskušate razglasiti razred povzetek in dokončno. Prevajalnik se na primer pritožuje izvleček zaključni razred Oblika ker abstraktnega razreda ni mogoče ustvariti in končnega razreda ni mogoče razširiti. Prevajalnik poroča tudi o napaki, ko prijavite metodo povzetek vendar ne razglasite njegovega razreda povzetek. Odstranjevanje povzetek Iz Oblika glava razreda na seznamu 3 bi na primer povzročila napako. To bi bila napaka, ker ne-abstraktnega (konkretnega) razreda ni mogoče ustvariti primerka, če vsebuje abstraktno metodo. Nazadnje, ko razširite abstraktni razred, mora razširitveni razred preglasiti vse abstraktne metode, sicer pa je treba razširitveni razred razglasiti za abstraktnega; v nasprotnem primeru bo prevajalnik sporočil napako.

Abstraktni razred lahko poleg ali namesto abstraktnih metod prijavi polja, konstruktorje in ne abstraktne metode. Na primer povzetek Vozilo razred lahko navede polja, ki opisujejo njegovo znamko, model in leto. Prav tako lahko razglasi konstruktor za inicializacijo teh polj in konkretne metode za vrnitev njihovih vrednosti. Oglejte si seznam 4.

Seznam 4. Povzemanje vozila

abstraktni razred Vozilo {private String make, model; zasebno int leto; Vozilo (znamka niza, model niza, int leto) {this.make = znamka; this.model = model; this.year = leto; } String getMake () {return make; } String getModel () {return model; } int getYear () {vrnjeno leto; } abstraktna void poteza (); }

To boste opazili Vozilo izjavlja izvleček premakni () metoda za opis gibanja vozila. Na primer, avto se povalja po cesti, čoln pluje po vodi in letalo leti po zraku. Vozilopodrazredi uporabnika bi preglasili premakni () in navedite ustrezen opis. Podedovali bi tudi metode, ki bi jih poklicali njihovi konstruktorji Voziloje konstruktor.

Downcasting in RTTI

Premik po hierarhiji razredov z upcastingom pomeni izgubo dostopa do funkcij podtipa. Na primer, dodelitev a Krog ugovarjati Oblika spremenljivka s pomeni, da je ne morete uporabljati s poklicati Krogje getRadius () metoda. Vendar je mogoče še enkrat dostopati Krogje getRadius () z izvedbo metode eksplicitna operacija oddaje kot je ta: Krog c = (Krog) s;.

Ta naloga je znana kot downcasting ker zapuščate hierarhijo dedovanja iz nadtipa v podtip (iz Oblika superrazred za Krog podrazred). Čeprav je upcast vedno varen (vmesnik superklase je podmnožica vmesnika podrazreda), downcast ni vedno varen. Seznam 5 prikazuje, kakšne težave bi lahko nastale, če nepravilno uporabljate prenašanje.

Seznam 5. Težava s prenašanjem

razred Superclass {} class Podrazred razširja Superclass {void method () {}} javni razred BadDowncast {public static void main (String [] args) {Superclass superclass = new Superclass (); Podrazred podrazred = (podrazred) nadrazred; subclass.method (); }}

Seznam 5 predstavlja hierarhijo razredov, ki jo sestavljajo Superrazred in Podrazred, ki se razteza Superrazred. Poleg tega Podrazred izjavlja metoda (). Tretji razred z imenom BadDowncast zagotavlja a glavni () metoda, ki ustvari primerek Superrazred. BadDowncast nato poskuša ta predmet znižati na Podrazred in rezultat dodelite spremenljivki podrazred.

V tem primeru se prevajalnik ne bo pritožil, ker je prenašanje iz superklase v podrazred v istovrstni hierarhiji zakonito. Če pa je bila dodelitev dodeljena, se bo aplikacija poskusila zrušiti subclass.method ();. V tem primeru bi JVM poskušal poklicati neobstoječo metodo, ker Superrazred se ne izjavi metoda (). Na srečo JVM preveri, ali je igralska zasedba zakonita, preden izvede operacijo igralske zasedbe. Odkrivanje tega Superrazred se ne izjavi metoda (), vrglo bi ClassCastException predmet. (O izjemah bom razpravljal v prihodnjem članku.)

Sestavite seznam 5, kot sledi:

javac BadDowncast.java

Zaženite nastalo aplikacijo:

java BadDowncast
$config[zx-auto] not found$config[zx-overlay] not found