Izpratne par tipu savietojamību ir būtiska, lai rakstītu labas Java programmas, taču nezinātājiem var šķist ļoti akadēmiska Java valodu elementu dispersiju mijiedarbība. Šis raksts ir paredzēts programmatūras izstrādātājiem, kas gatavi risināt šo problēmu! 1. daļa atklāj kovariētās un pretrunīgās attiecības starp vienkāršākiem elementiem, piemēram, masīvu tipiem un vispārīgiem tipiem, kā arī īpašo Java valodas elementu, aizstājējzīmi. 2. daļā tiek pētīta tipu atkarība un dispersija kopējos API piemēros un lambda izteiksmēs.
lejupielādēt Lejupielādēt avotu Iegūstiet šī raksta "Tipa atkarība Java 1. daļā" avota kodu. JavaWorld izveidoja Dr Andreas Solymosi.Jēdzieni un terminoloģija
Pirms nonākam kovariācijas un pretrunīguma attiecībās starp dažādiem Java valodas elementiem, būsim pārliecināti, ka mums ir kopīgs konceptuāls ietvars.
Saderība
Objektorientētā programmēšanā saderība attiecas uz virzītu saistību starp tipiem, kā parādīts 1. attēlā.
Andreas Solymosi Mēs sakām, ka divi veidi ir savietojams Java, ja ir iespējams pārsūtīt datus starp tipu mainīgajiem. Datu pārsūtīšana ir iespējama, ja kompilators to akceptē un to veic, izmantojot piešķiršanu vai parametru nodošanu. Kā piemērs, īss
ir savietojams ar int
jo norīkojums intVariaable = īss Mainīgais;
ir iespējams. Bet būla
nav saderīgs ar int
jo norīkojums intVariable = būla mainīgais;
nav iespējams; sastādītājs to nepieņems.
Tā kā saderība dažkārt ir vērsta saistība T1
ir savietojams ar T2
bet T2
nav saderīgs ar T1
, vai ne tādā pašā veidā. Mēs to redzēsim tālāk, kad apspriedīsim tiešu vai netiešu saderību.
Svarīgi ir tas, ka ir iespējama savietojamība starp atsauces tipiem tikai tipa hierarhijas ietvaros. Visi klases veidi ir saderīgi ar Objekts
, piemēram, tāpēc, ka visas klases netieši manto no Objekts
. Vesels skaitlis
nav saderīgs ar Peldēt
tomēr tāpēc Peldēt
nav grupas superklase Vesels skaitlis
. Vesels skaitlis
ir savietojams ar Skaits
, jo Skaits
ir (abstrakta) Vesels skaitlis
. Tā kā tie atrodas viena tipa hierarhijā, kompilators pieņem uzdevumu numberReference = integerReference;
.
Mēs runājam par netieši vai nepārprotams saderība atkarībā no tā, vai savietojamība ir skaidri jāmarķē vai nē. Piemēram, īss ir netieši savietojams ar int
(kā parādīts iepriekš), bet ne otrādi: uzdevums shortVariable = mainīgs;
nav iespējams. Tomēr īss ir nepārprotami savietojams ar int
, jo uzdevums shortVariaable = (īss) mainīgs;
ir iespējams. Šeit mums jāatzīmē saderība ar liešana, kas pazīstams arī kā tipa pārveidošana.
Līdzīgi starp atsauces veidiem: integerReference = numursReference;
nav pieņemams, tikai integerReference = (Integer) skaitlisReference;
tiktu pieņemts. Tāpēc Vesels skaitlis
ir netieši savietojams ar Skaits
bet Skaits
ir tikai nepārprotami savietojams ar Vesels skaitlis
.
Atkarība
Veids var būt atkarīgs no citiem veidiem. Piemēram, masīva tips int []
atkarīgs no primitīvā tipa int
. Līdzīgi arī vispārīgais tips ArrayList
ir atkarīgs no veida Klients
. Metodes var būt atkarīgas arī no veida, atkarībā no to parametru veidiem. Piemēram, metode tukšs pieaugums (vesels skaitlis i)
; atkarīgs no veida Vesels skaitlis
. Dažas metodes (piemēram, daži vispārīgi veidi) ir atkarīgas no vairāk nekā viena veida, piemēram, metodes, kurām ir vairāki parametri.
Kovārija un pretrunīgums
Kovariancija un pretrunīgums nosaka saderību, pamatojoties uz tipiem. Jebkurā gadījumā dispersija ir virzīta attiecība. Kovariance var tulkot kā "atšķirīgi tajā pašā virzienā" vai ar-atšķirīgs, tā kā pretrunīgums nozīmē "atšķirīgs pretējā virzienā" vai pret-atšķirīgs. Kovariāna un kontravarianta veidi nav vienādi, taču starp tiem pastāv korelācija. Nosaukumi nozīmē korelācijas virzienu.
Tātad, kovariācija nozīmē, ka divu veidu savietojamība nozīmē no tiem atkarīgo tipu savietojamību. Ņemot vērā tipu savietojamību, var pieņemt, ka atkarīgie tipi ir atšķirīgi, kā parādīts 2. attēlā.
Andreas Solymosi Programmas saderība T1
uz T2
nozīmē saderību ar A (T.1
) līdz A (T.2
). Atkarīgais tips A (T)
tiek saukts kovariāns; vai precīzāk, A (T.1
) ir līdzīgs A (T.2
).
Vēl viens piemērs: tāpēc, ka uzdevums numberArray = integerArray;
ir iespējams (vismaz Java valodā), masīvu veidi Vesels skaitlis[]
un Skaits []
ir mainīgi. Tātad, mēs to varam teikt Vesels skaitlis[]
ir netieši kovārijs uz Skaits []
. Un, lai gan nav otrādi - uzdevums integerArray = skaitlisArray;
nav iespējams - norīkojums ar tipa liešanu (integerArray = (Integer []) skaitlis Array;
) ir iespējams; tāpēc mēs sakām: Skaits []
ir nepārprotami kovārijs uz Vesels skaitlis[]
.
Apkopot: Vesels skaitlis
ir netieši savietojams ar Skaits
tāpēc Vesels skaitlis[]
ir netieši kopīgs ar Skaits []
, un Skaits []
ir nepārprotami kopīgs ar Vesels skaitlis[]
. 3. attēls ilustrē.
Vispārīgi runājot, mēs varam teikt, ka masīvu veidi Java ir atšķirīgi. Kovariācijas piemēri starp vispārīgiem tipiem tiks aplūkoti vēlāk rakstā.
Pretrunīgums
Tāpat kā kovariācija, arī pretrunīgums ir a vadīts attiecības. Kaut arī kovariācija nozīmē ar-atšķirīgs, pretrunīgums nozīmē pret-atšķirīgs. Kā jau iepriekš minēju, nosaukumi izsaka korelācijas virzienu. Ir arī svarīgi atzīmēt, ka dispersija nav tipu atribūts, bet gan tikai atkarīgs veidi (piemēram, masīvi un vispārīgi veidi, kā arī metodes, kuras es aplūkošu 2. daļā).
Atkarīgs tips, piemēram, A (T)
tiek saukts pretrunīgs ja saderība ar T1
uz T2
nozīmē saderību ar A (T.2
) līdz A (T.1
). 4. attēls ilustrē.
Valodas elements (tips vai metode) A (T)
atkarībā no T
ir kovariāns ja saderība ar T1
uz T2
nozīmē saderību ar A (T.1
) līdz A (T.2
). Ja saderība T1
uz T2
nozīmē saderību ar A (T.2
) līdz A (T.1
), tad tips A (T)
ir pretrunīgs. Ja saderība T1
starp T2
nenozīmē nekādu savietojamību starp A (T.1
) un A (T.2
), tad A (T)
ir nemainīgs.
Masīvu veidi Java nav netieši pretrunīgi, bet tie var būt skaidri pretrunīgi , tāpat kā vispārīgie veidi. Piedāvāšu dažus piemērus vēlāk rakstā.
No tipa atkarīgi elementi: metodes un veidi
Java valodā no tipa atkarīgie elementi ir metodes, masīvu veidi un vispārīgi (parametrizēti) tipi. Metodes ir atkarīgas no to parametru veidiem. Masīva tips, T []
, ir atkarīgs no tā elementu veidiem, T
. Vispārējs veids G
ir atkarīgs no tā tipa parametra, T
. 5. attēls ilustrē.
Pārsvarā šajā rakstā galvenā uzmanība tiek pievērsta tipu savietojamībai, lai gan 2. daļas beigās es pieskaršos metožu saderībai.
Netieša un skaidra tipa savietojamība
Agrāk jūs redzējāt tipu T1
būtne netieši (vai nepārprotami) savietojams ar T2
. Tas ir taisnība tikai tad, ja tiek piešķirts tipa mainīgais T1
uz tipa mainīgo T2
ir atļauts bez (vai ar) marķēšanas. Tipa apraide ir visizplatītākais veids, kā atzīmēt skaidru saderību:
variableOfTypeT2 = variableOfTypeT1; // netiešais saderīgais mainīgaisOfTypeT2 = (T2) variableOfTypeT1; // nepārprotami savietojams
Piemēram, int
ir netieši savietojams ar ilgi
un skaidri saderīgs ar īss
:
int mainīgais = 5; long longVariaable = intVariable; // netieši saderīgs īss īss mainīgais = (īss) mainīgs; // nepārprotami savietojams
Netieša un skaidra saderība pastāv ne tikai uzdevumos, bet arī parametru pārsūtīšanā no metodes izsaukuma uz metodes definīciju un atpakaļ. Kopā ar ievades parametriem tas nozīmē arī funkcijas rezultāta nodošanu, ko jūs darītu kā izvades parametru.
Pieraksti to būla
nav saderīgs ar jebkuru citu tipu, kā arī primitīvs un atsauces tips nekad nevar būt saderīgi.
Metodes parametri
Mēs sakām, metode nolasa ievades parametrus un raksta izvades parametrus. Primitīvu tipu parametri vienmēr ir ievades parametri. Funkcijas atgriešanās vērtība vienmēr ir izejas parametrs. Atsauces tipu parametri var būt abi: ja metode maina atsauci (vai primitīvu parametru), izmaiņas paliek metodē (tas nozīmē, ka pēc zvana tās nav redzamas ārpus metodes - tas ir pazīstams kā zvans pēc vērtības). Ja metode maina minēto objektu, tomēr izmaiņas paliek pēc atgriešanās no metodes - tas ir pazīstams kā zvans pēc atsauces.
(Atsauces) apakštips ir netieši savietojams ar tā supertipu, un supertips ir tieši saderīgs ar tā apakštipu. Tas nozīmē, ka atsauces tipi ir saderīgi tikai to hierarhijas nozarē - netieši uz augšu un tieši uz leju:
referenceOfSuperType = referenceOfSubType; // netiešā saderīgā atsauceOfSubType = (SubType) atsauceOfSuperType; // nepārprotami savietojams
Java kompilators parasti pieļauj netiešu saderību ar uzdevumu tikai ja pastāv risks zaudēt informāciju izpildlaika laikā starp dažādiem tipiem. (Tomēr ņemiet vērā, ka šī kārtula nav derīga, lai zaudētu precizitāti, piemēram, uzdevumā no int
peldēt.) Piemēram, int
ir netieši savietojams ar ilgi
jo a ilgi
mainīgais tur katru int
vērtība. Turpretī a īss
mainīgajam nav neviena int
vērtības; tādējādi starp šiem elementiem ir atļauta tikai skaidra savietojamība.
Ņemiet vērā, ka netiešā saderība 6. attēlā pieņem, ka saistība ir pārejošs: īss
ir savietojams ar ilgi
.
Līdzīgi tam, kā redzat 6. attēlā, vienmēr ir iespējams piešķirt apakštipa atsauci int
supertipa atsauce. Paturiet prātā, ka tas pats uzdevums otrā virzienā varētu iemest a ClassCastException
, tāpēc Java kompilators to pieļauj tikai ar tipu liešanu.
Kovariāts un pretrunas masīvu tipiem
Java daži masīvu veidi ir kovarianti un / vai pretrunīgi. Kovariācijas gadījumā tas nozīmē, ka, ja T
ir savietojams ar U
, pēc tam T []
ir saderīgs arī ar U []
. Pretrunu gadījumā tas nozīmē U []
ir savietojams ar T []
. Primitīvu veidu masīvi Java ir nemainīgi:
longArray = intArray; // tipa kļūda shortArray = (īss []) intArray; // tipa kļūda
Atsauces tipu masīvi ir netieši kovārijs un skaidri pretrunīgitomēr:
SuperType [] superArray; SubType [] subArray; ... superArray = subArray; // implicit covariant subArray = (SubType []) superArray; // nepārprotams pretrunīgs
Andreas Solymosi 7. attēls. Netiešā kovariācija masīviem
Tas praktiski nozīmē to, ka masīva komponentu piešķiršana varētu mest ArrayStoreException
izpildlaika laikā. Ja masīva atsauce uz SuperType
atsauces uz masīva objektu Apakštips
, un pēc tam vienu no tā komponentiem piešķir a SuperType
objektu, tad:
superArray [1] = jauns SuperType (); // iemet ArrayStoreException
To dažreiz sauc par kovariācijas problēma. Patiesā problēma ir ne tik daudz izņēmums (no kura varētu izvairīties, programmējot disciplīnu), bet gan tas, ka virtuālajai mašīnai izpildlaikā jāpārbauda katrs masīva elementa uzdevums. Tas padara Java efektivitāti neizdevīgāku salīdzinājumā ar valodām bez kovariances (kur ir aizliegts saderīgs masīva atsauču piešķiršana) vai tādām valodām kā Scala, kur kovarianciju var izslēgt.
Kovariances piemērs
Vienkāršā piemērā masīva atsauce ir veida Objekts []
bet masīva objekts un elementi ir dažādās klasēs:
Object [] objectArray; // masīva atsauces objektsArray = jauna virkne [3]; // masīva objekts; saderīgs piešķiršanas objekts Array [0] = jauns vesels skaitlis (5); // iemet ArrayStoreException
Kovariācijas dēļ sastādītājs nevar pārbaudīt masīva elementu pēdējā piešķīruma pareizību - JVM to dara un ar ievērojamiem izdevumiem. Tomēr kompilators var optimizēt izdevumus, ja starp masīvu tipiem netiek izmantota veidu saderība.
Andreas SolymosiAtcerieties, ka Java valodā dažu veidu atsauces mainīgajiem ir aizliegts atsaukties uz tā supertipa objektu: 8. attēlā redzamās bultiņas nedrīkst būt vērstas uz augšu.
Varianti un aizstājējzīmes vispārīgos veidos
Vispārējie (parametrizētie) tipi ir netieši nemainīgs Java valodā, kas nozīmē, ka dažādi vispārēja veida eksemplāri nav savstarpēji savietojami. Pat veida liešana neizraisīs saderību:
Vispārējs superGeneric; Generic subGeneric; subGeneric = (Vispārīgi) superGeneric; // tipa kļūda superGeneric = (Generic) subGeneric; // tipa kļūda
Tipa kļūdas rodas, lai arī subGeneric.getClass () == superGeneric.getClass ()
. Problēma ir tā, ka metode getClass ()
nosaka neapstrādāto tipu - tāpēc tipa parametrs nepieder pie metodes paraksta. Tādējādi abu metožu deklarācijas
void metode (Generic p); void metode (Generic p);
nedrīkst rasties kopā saskarnes (vai abstraktas klases) definīcijā.