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:
- 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.
- 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). - 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 adubultā
vērtība) un metode var atgriezt aDubultā
vienā objektā, un tas pats lauks varētu būt veidaStīga
un tā pati metode var atgriezt aStīga
citā objektā. Java atbalsta parametru polimorfismu, izmantojot vispārīgus, par kuriem es runāšu nākamajā rakstā. - 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 aizdarīt ()
metode; ieviešotAplis
,Taisnstūris
un citas apakšklases, kas ignorēizdarīt ()
; ieviešot tipa masīvuForma
kuru elementi glabā atsaucesForma
apakšklases gadījumi; un zvanotForma
'sizdarīt ()
metodi katrā gadījumā. Kad zvanātizdarīt ()
, tas irAplis
,Taisnstūris
vai citiForma
instancesizdarīt ()
metode, kas tiek saukta. Mēs sakām, ka ir daudz veiduForma
'sizdarī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 Aplis
specifiskās metodes nav daļa no Forma
saskarne. 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 1
tomē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īdzeklis
apakšklases tiktu ignorētas pārvietot()
un sniedz atbilstošu aprakstu. Viņi arī pārmantos metodes un to konstruktori izsauks Transportlīdzeklis
konstruktors.
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