Programmēšana

Java polimorfisms un tā veidi

Polimorfisms attiecas uz dažu entītiju spēju notikt dažādās formās. Tautā to pārstāv tauriņš, kas morfē no kāpura līdz pupa līdz imago. Polimorfisms pastāv arī programmēšanas valodās kā modelēšanas paņēmiens, kas ļauj izveidot vienu saskarni dažādiem operandiem, argumentiem un objektiem. Java polimorfisma rezultāts ir kodolīgāks un vieglāk uzturams kods.

Kaut arī šī apmācība ir vērsta uz apakštipa polimorfismu, jums vajadzētu zināt vairākus citus veidus. Mēs sāksim ar pārskatu par visiem četriem polimorfisma veidiem.

lejupielādēt Iegūt kodu Lejupielādējiet šajā apmācībā avota kodu, piemēram, lietojumprogrammām. Izveidoja Jeff Friesen JavaWorld.

Java polimorfisma veidi

Java ir četru veidu polimorfisms:

  1. Piespiešana ir darbība, kas kalpo vairākiem veidiem, izmantojot netiešā tipa pārveidošanu. Piemēram, veselu skaitli dalāt ar citu veselu skaitli vai peldošā komata vērtību ar citu peldošā komata vērtību. Ja viens operands ir vesels skaitlis un otrs operands ir peldošā komata vērtība, kompilators piespiešanas (netieši pārveido) veselu skaitli peldošā komata vērtībā, lai novērstu tipa kļūdu. (Nav tādas dalīšanas operācijas, kas atbalstītu vesela skaitļa operandu un peldošā komata operandu.) Vēl viens piemērs ir apakšklases objekta atsauces nodošana metodes virsklases parametram. Sastādītājs piespiež apakšklases tipu virsklases tipam, lai darbības ierobežotu tikai ar virsklases darbību.
  2. Pārslodze attiecas uz viena un tā paša operatora simbola vai metodes nosaukuma izmantošanu dažādos kontekstos. Piemēram, jūs varat izmantot + lai veiktu vesela skaitļa saskaitīšanu, peldošā komata pievienošanu vai virkņu savienošanu atkarībā no tā operandu veidiem. Arī klasē var parādīties vairākas metodes ar vienādu nosaukumu (izmantojot deklarāciju un / vai mantojumu).
  3. Parametrisks polimorfisms nosaka, ka klases deklarācijā lauka nosaukums var būt saistīts ar dažādiem tipiem, bet metodes nosaukums - ar dažādiem parametru un atgriešanās tipiem. Tad laukam un metodei katrā klases instancē (objektā) var būt dažādi veidi. Piemēram, lauks var būt veida Dubultā (Java standarta klases bibliotēkas loceklis, kas aptin a dubultā vērtība) un metode var atgriezt a Dubultā vienā objektā, un tas pats lauks varētu būt veida Stīga un tā pati metode var atgriezt a Stīga citā objektā. Java atbalsta parametru polimorfismu, izmantojot vispārīgus, par kuriem es runāšu nākamajā rakstā.
  4. Apakštips nozīmē, ka tips var kalpot kā cita tipa apakštips. Kad apakštipa eksemplārs parādās supertipa kontekstā, izpildot supertipa darbību apakštipa instancē, tiek iegūta šīs operācijas izpildes apakštipa versija. Piemēram, ņemiet vērā koda fragmentu, kas zīmē patvaļīgas formas. Jūs varat izteikt šo zīmēšanas kodu kodolīgāk, ieviešot a Forma klase ar a izdarīt () metode; ieviešot Aplis, Taisnstūrisun citas apakšklases, kas ignorē izdarīt (); ieviešot tipa masīvu Forma kuru elementi glabā atsauces Forma apakšklases gadījumi; un zvanot Forma's izdarīt () metodi katrā gadījumā. Kad zvanāt izdarīt (), tas ir Aplis, Taisnstūrisvai citi Forma instances izdarīt () metode, kas tiek saukta. Mēs sakām, ka ir daudz veidu Forma's izdarīt () metodi.

Šī apmācība iepazīstina ar apakštipa polimorfismu. Jūs uzzināsiet par augšupielādi un novēlotu saistīšanu, abstraktām klasēm (kuras nevar instantizēt) un abstraktām metodēm (kuras nevar izsaukt). Jūs arī uzzināsiet par downcasting un izpildlaika tipa identifikāciju, un jūs vispirms apskatīsit kovariānos atgriešanās veidus. Es saglabātu parametru polimorfismu nākotnes apmācībai.

Ad-hoc vs universālais polimorfisms

Tāpat kā daudzus izstrādātājus, es klasificēju piespiešanu un pārslodzi kā ad-hoc polimorfismu, bet parametrisko un apakštipu - kā universālo polimorfismu. Lai arī vērtīgas metodes, es neuzskatu, ka piespiešana un pārslodze ir patiess polimorfisms; tie drīzāk ir tipa pārveidojumi un sintaktiskais cukurs.

Apakštipa polimorfisms: satricinājums un novēlota saistīšanās

Apakštipa polimorfisms balstās uz satricināšanu un novēlotu saistīšanos. Augšupeja ir liešanas veids, kad jūs mantojuma hierarhiju izveidojat no apakštipa uz supertipu. Neviens dalībnieks nav iesaistīts, jo apakštips ir supertipa specializācija. Piemēram, Forma s = jauns aplis (); upcasts no Aplis uz Forma. Tam ir jēga, jo aplis ir sava veida forma.

Pēc satricināšanas Aplis uz Forma, jūs nevarat piezvanīt Aplis- specifiskas metodes, piemēram, a getRadius () metode, kas atgriež apļa rādiusu, jo Aplisspecifiskās metodes nav daļa no Formasaskarne. Zaudēt piekļuvi apakštipa pazīmēm pēc apakšklases sašaurināšanas tā virsklasei šķiet bezjēdzīga, taču tā ir nepieciešama apakštipa polimorfisma sasniegšanai.

Pieņemsim, ka tā Forma paziņo a izdarīt () metode, tā Aplis apakšklase ignorē šo metodi, Forma s = jauns aplis (); ir tikko izpildījis, un nākamajā rindā ir norādīts s.zīmēt ();. Kurš izdarīt () metodi sauc: Forma's izdarīt () metode vai Aplis's izdarīt () metodi? Sastādītājs nezina, kurš izdarīt () metode zvanīšanai. Viss, ko tas var darīt, ir pārbaudīt, vai virsklasē pastāv metode, un pārbaudīt, vai metodes izsaukuma argumentu saraksts un atgriešanās tips sakrīt ar superklases metožu deklarāciju. Tomēr kompilators kompilētajā kodā ievieto arī instrukciju, kas izpildlaika laikā ielādē un izmanto jebkuru atsauci, kas atrodas s lai izsauktu pareizo izdarīt () metodi. Šis uzdevums ir pazīstams kā vēlīnā iesiešana.

Vēlā saistīšana pret agrīnu saistīšanu

Vēlā saistīšana tiek izmantota zvaniem uzgalīgais gadījumu metodes. Visiem pārējiem metožu izsaukumiem kompilators zina, kuru metodi izsaukt. Kompilētajā kodā tā ievieto instrukciju, kas izsauc metodi, kas saistīta ar mainīgā tipu, nevis tā vērtību. Šī tehnika ir pazīstama kā agrīna iesiešana.

Esmu izveidojis lietojumprogrammu, kas demonstrē apakštipa polimorfismu attiecībā uz augšupielādi un novēlotu saistīšanu. Šis pieteikums sastāv no Forma, Aplis, Taisnstūris, un Formas klases, kur katra klase tiek glabāta savā avota failā. 1. saraksts parāda pirmās trīs klases.

Uzskaitīšana 1. Formu hierarhijas deklarēšana

klases forma {void draw () {}} klases aplis paplašina formu {private int x, y, r; Aplis (int x, int y, int r) {this.x = x; tas.y = y; this.r = r; } // Īsuma labad esmu izlaidis metodes getX (), getY () un getRadius (). @Orride void draw () {System.out.println ("Zīmēšanas aplis (" + x + "," + y + "," + r + ")"); }} klases taisnstūris paplašina formu {private int x, y, w, h; Taisnstūris (int x, int y, int w, int h) {this.x = x; tas.y = y; this.w = w; tas.h = h; } // Īsuma labad esmu izlaidis metodes getX (), getY (), getWidth () un getHeight () //. @Orride void draw () {System.out.println ("Zīmēšanas taisnstūris (" + x + "," + y + "," + w + "," + h + ")"); }}

Sarakstā 2 parādīts Formas aplikācijas klase, kuras galvenais () metode virza lietojumprogrammu.

Uzskaitīšana 2. Apgrūtināšana un novēlota saistīšanās apakštipa polimorfismā

klases formas {public static void main (String [] args) {Shape [] formas = {jauns aplis (10, 20, 30), jauns taisnstūris (20, 30, 40, 50)}; par (int i = 0; i <formas.garums; i ++) formām [i] .zīmēt (); }}

Deklarācija formas masīvs demonstrē satricinājumu. The Aplis un Taisnstūris atsauces tiek saglabātas formas [0] un formas [1] un ir nepieejami rakstīšanai Forma. Katrs no formas [0] un formas [1] tiek uzskatīts par Forma gadījums: formas [0] netiek uzskatīts par Aplis; formas [1] netiek uzskatīts par Taisnstūris.

Novēlotu saistīšanos pierāda formas [i] .zīmēt (); izteiksme. Kad i ir vienāds 0, kompilatora ģenerētā instrukcija izraisa Aplis's izdarīt () metodi. Kad i ir vienāds 1tomēr šī instrukcija izraisa Taisnstūris's izdarīt () metodi. Šī ir apakštipa polimorfisma būtība.

Pieņemot, ka visi četri avota faili (Formas.java, Forma.java, Taisnstūris.java, un Circle.java) atrodas pašreizējā direktorijā, apkopojiet tos, izmantojot kādu no šīm komandrindām:

javac * .java javac Shapes.java

Palaidiet iegūto lietojumprogrammu:

java Shapes

Jums jāievēro šāda izeja:

Zīmēšanas aplis (10, 20, 30) Zīmēšanas taisnstūris (20, 30, 40, 50)

Abstraktas klases un metodes

Veidojot klases hierarhijas, jūs atradīsit, ka klases, kas atrodas tuvāk šo hierarhiju augšdaļai, ir vispārīgākas nekā klases, kas atrodas zemāk. Piemēram, a Transportlīdzeklis superklase ir vispārīgāka nekā a Smagā mašīna apakšklase. Līdzīgi a Forma superklase ir vispārīgāka nekā a Aplis vai a Taisnstūris apakšklase.

Nav jēgas iniciēt sugas klasi. Galu galā, kas būtu a Transportlīdzeklis objektu aprakstīt? Līdzīgi, kāda veida formu attēlo a Forma objekts? Nevis kodē tukšu izdarīt () metode Forma, mēs varam novērst šīs metodes izsaukšanu un šīs klases instancēšanu, pasludinot abas entītijas par abstraktām.

Java nodrošina abstrakts rezervēts vārds, lai pasludinātu klasi, kuru nevar instantizēt. Sastādītājs ziņo par kļūdu, mēģinot instantizēt šo klasi. abstrakts tiek izmantots arī, lai deklarētu metodi bez ķermeņa. The izdarīt () metodei nav vajadzīgs ķermenis, jo tā nespēj uzzīmēt abstraktu formu. 3. saraksts parāda.

Saraksts 3. Formas klases un tās zīmēšanas () metodes abstrakcija

abstrakta klase Forma {abstract void draw (); // ir nepieciešams semikols}

Abstrakti brīdinājumi

Sastādītājs ziņo par kļūdu, mēģinot deklarēt klasi abstrakts un galīgais. Piemēram, sastādītājs sūdzas abstrakts gala klases forma jo abstraktu klasi nevar instantizēt un galīgo klasi nevar pagarināt. Kompilators ziņo arī par kļūdu, deklarējot metodi abstrakts bet nedeklarē tās klasi abstrakts. Noņemšana abstrakts no Forma klases galvene 3. sarakstā radītu kļūdu, piemēram. Tā būtu kļūda, jo nav abstrakta (konkrēta) klase, kuru nevar abstraktēt, ja tajā ir abstrakta metode. Visbeidzot, paplašinot abstrakto klasi, paplašinošajai klasei ir jāpārņem visas abstraktās metodes, pretējā gadījumā pati paplašinātā klase ir jāpasludina par abstraktu; pretējā gadījumā sastādītājs ziņos par kļūdu.

Abstraktā klase papildus abstraktām metodēm vai to vietā var deklarēt laukus, konstruktorus un ne abstraktas metodes. Piemēram, abstrakts Transportlīdzeklis klase varētu deklarēt laukus, kas apraksta tā marku, modeli un gadu. Tas var arī paziņot, ka konstruktors inicializē šos laukus, un konkrētas metodes, lai atgrieztu to vērtības. Pārbaudiet 4. sarakstu.

Saraksts 4. Transportlīdzekļa abstrakcija

abstraktā klase Transportlīdzeklis {privāts Stīgu marka, modelis; privāts int gads; Transportlīdzeklis (virknes marka, virknes modelis, gada gads) {this.make = marka; this.model = modelis; tas.gads = gads; } Virkne getMake () {return make; } String getModel () {atgriešanās modelis; } int getYear () {atgriešanās gads; } abstrakts void pārvietoties (); }

Jūs to atzīmēsiet Transportlīdzeklis paziņo abstraktu pārvietot() metode transportlīdzekļa kustības aprakstam. Piemēram, automašīna ripo pa ceļu, laiva brauc pāri ūdenim, un lidmašīna lido pa gaisu. Transportlīdzeklisapakšklases tiktu ignorētas pārvietot() un sniedz atbilstošu aprakstu. Viņi arī pārmantos metodes un to konstruktori izsauks Transportlīdzekliskonstruktors.

Downcasting un RTTI

Pārejot klases hierarhijā augšup, izmantojot augšupielādi, tiek zaudēta piekļuve apakštipa iezīmēm. Piemēram, piešķirot a Aplis iebilst pret Forma mainīgais s nozīmē, ka jūs nevarat izmantot s zvanīt Aplis's getRadius () metodi. Tomēr ir iespējams vēlreiz piekļūt Aplis's getRadius () metodi, veicot skaidra dalībnieku darbība kā šis: Aplis c = (aplis) s;.

Šis uzdevums ir pazīstams kā pazemināšana jo jūs atmetat mantojuma hierarhiju no supertipa uz apakštipu (no Forma klases superklase Aplis apakšklase). Lai gan augšupeja vienmēr ir droša (augstākās klases saskarne ir apakšklases saskarnes apakškopa), lejupvērstā versija ne vienmēr ir droša. 5. saraksts parāda, kādas nepatikšanas var rasties, ja nepareizi izmantojat downcasting.

Uzskaitīšana 5. Problēma ar downcasting

klases Superclass {} klases apakšklase paplašina Superclass {void method () {}} public class BadDowncast {public static void main (String [] args) {Superclass superclass = new Superclass (); Apakšklases apakšklase = (Apakšklase) superklase; apakšklase.metode (); }}

5. saraksts parāda klases hierarhiju, kas sastāv no Superklase un Apakšklase, kas stiepjas Superklase. Turklāt Apakšklase paziņo metode (). Trešā klase nosaukta BadDowncast nodrošina a galvenais () metode, kas instantizē Superklase. BadDowncast tad mēģina pazemināt šo objektu Apakšklase un piešķirt rezultātu mainīgajam apakšklase.

Šajā gadījumā sastādītājs nesūdzēsies, jo tāda paša veida hierarhijas pazemināšana no superklases uz apakšklasi ir likumīga. Tas nozīmē, ka, ja uzdevums būtu atļauts, lietojumprogramma avarētu, mēģinot to izpildīt apakšklase.metode ();. Šajā gadījumā JVM mēģinātu izsaukt neeksistējošu metodi, jo Superklase nedeklarē metode (). Par laimi, JVM pirms apraides operācijas veikšanas pārbauda, ​​vai dalībnieki ir likumīgi. Atklājot to Superklase nedeklarē metode (), tas iemestu a ClassCastException objekts. (Es izņēmumus apspriedīšu nākamajā rakstā.)

Sastādiet 5. sarakstu šādi:

javac BadDowncast.java

Palaidiet iegūto lietojumprogrammu:

java BadDowncast