Programavimas

Tipo priklausomybė „Java“, 1 dalis

Suprasti tipų suderinamumą yra esminis dalykas rašant geras „Java“ programas, tačiau nežinantiesiems „Java“ kalbos elementų skirtumų sąveika gali pasirodyti labai akademinė. Šis straipsnis skirtas programinės įrangos kūrėjams, pasirengusiems įveikti iššūkį! 1 dalis atskleidžia kovariantiškus ir kontraversiškus ryšius tarp paprastesnių elementų, tokių kaip masyvo tipai ir bendriniai tipai, taip pat specialaus „Java“ kalbos elemento, pakaitos. 2 dalyje nagrinėjama tipų API pavyzdžių ir lambda išraiškų priklausomybė nuo tipų.

atsisiųsti Atsisiųskite šaltinį Gaukite šio straipsnio „Tipo priklausomybė„ Java “1 dalyje“ šaltinio kodą. „JavaWorld“ sukūrė dr. Andreas Solymosi.

Sąvokos ir terminologija

Prieš pradėdami kovariacijos ir prieštaringumo santykius tarp įvairių „Java“ kalbos elementų, įsitikinkime, kad turime bendrą konceptualų pagrindą.

Suderinamumas

Į objektinį programavimą, suderinamumas reiškia nukreiptą santykį tarp tipų, kaip parodyta 1 paveiksle.

Andreasas Solymosi

Mes sakome, kad yra du tipai suderinamas „Java“, jei įmanoma perduoti duomenis tarp tipų kintamųjų. Duomenų perdavimas yra įmanomas, jei kompiliatorius su tuo sutinka ir yra atliekamas perduodant ar perduodant parametrus. Pavyzdžiui, trumpas yra suderinamas su tarpt nes užduotis intVariable = trumpasKintamas; yra įmanoma. Bet loginis nėra suderinamas su tarpt nes užduotis intVariable = loginisKintamasis; neįmanoma; kompiliatorius to nepriims.

Nes suderinamumas kartais yra nukreiptas santykis T1 yra suderinamas su T2 bet T2 nėra suderinamas su T1, arba ne tokiu pačiu būdu. Tai pamatysime toliau, kai aptarsime aiškų ar numanomą suderinamumą.

Svarbu tai, kad įmanoma suderinti referencinius tipus tik tipo hierarchijoje. Visi klasių tipai yra suderinami su Objektas, pavyzdžiui, todėl, kad visos klasės netiesiogiai paveldi iš Objektas. Sveikasis skaičius nėra suderinamas su Plūdėvis dėlto, nes Plūdė nėra superklasė Sveikasis skaičius. Sveikasis skaičiusyra suderinamas su Skaičius, nes Skaičius yra (abstraktus) superklasė Sveikasis skaičius. Kompiliatorius priima užduotį, nes jie yra to paties tipo hierarchijoje numberReference = integerReference;.

Mes kalbame apie numanomas arba aiškus suderinamumas, atsižvelgiant į tai, ar suderinamumas turi būti aiškiai pažymėtas, ar ne. Pavyzdžiui, trumpas yra netiesiogiai suderinamas su tarpt (kaip parodyta aukščiau), bet ne atvirkščiai: užduotis trumpasis kintamasis = kintamasis; neįmanoma. Tačiau trumpas yra aiškiai suderinamas su tarpt, nes užduotis shortVariable = (trumpas) kintamasis; yra įmanoma. Čia turime pažymėti suderinamumą liejimas, taip pat žinomas kaip tipo konversija.

Panašiai tarp referencinių tipų: integerReference = skaičiusReference; nėra priimtinas integerReference = (Sveikasis skaičius) numberReference; būtų priimta. Todėl, Sveikasis skaičius yra netiesiogiai suderinamas su Skaičius bet Skaičius yra tik aiškiai suderinamas su Sveikasis skaičius.

Priklausomybė

Tipas gali priklausyti nuo kitų tipų. Pavyzdžiui, masyvo tipas int [] priklauso nuo primityvaus tipo tarpt. Panašiai ir bendrinis tipas „ArrayList“ priklauso nuo tipo Klientas. Metodai taip pat gali priklausyti nuo tipo, atsižvelgiant į jų parametrų tipus. Pavyzdžiui, metodas tuščias prieaugis (sveikasis skaičius i); priklauso nuo tipo Sveikasis skaičius. Kai kurie metodai (pvz., Kai kurie bendrieji tipai) priklauso nuo daugiau nei vieno tipo, pavyzdžiui, metodai, turintys daugiau nei vieną parametrą.

Kovariacija ir prieštaringumas

Kovariacija ir prieštaringumas lemia suderinamumą pagal tipus. Bet kuriuo atveju dispersija yra nukreiptas santykis. Kovarsija gali būti verčiamas kaip „skirtingi ta pačia kryptimi“ arba su-kitoks, kadangi prieštaringumas reiškia „skirtingi priešinga kryptimi“ arba prieš-skirtingi. Kovariantiniai ir kontraversiški tipai nėra vienodi, tačiau tarp jų yra koreliacija. Pavadinimai reiškia koreliacijos kryptį.

Taigi, kovariacija reiškia, kad dviejų tipų suderinamumas reiškia nuo jų priklausančių tipų suderinamumą. Atsižvelgiant į tipų suderinamumą, daroma prielaida, kad priklausomi tipai yra kintantys, kaip parodyta 2 paveiksle.

Andreasas Solymosi

Suderinamumas T1 į T2 reiškia suderinamumą A (T.1) iki A (T.2). Priklausomas tipas A (T) vadinamas kovariantas; arba tiksliau, A (T.1) yra nekintanti A (T.2).

Kitas pavyzdys: nes užduotis skaičiusArray = sveikasis skaičiusArray; yra įmanoma (bent jau Java), masyvo tipai Sveikas skaičius [] ir Skaičius [] yra kovariantai. Taigi, mes galime tai pasakyti Sveikas skaičius [] yra numanomai kovariantas į Skaičius []. Ir nors nėra priešingai - užduotis integerArray = skaičiusArray; neįmanoma - užduotis su tipo liejimu (integerArray = (Sveikasis skaičius []) skaičiusArray;) yra įmanoma; todėl mes sakome: Skaičius [] yra aiškiai kovariantai į Sveikas skaičius [] .

Apibendrinti: Sveikasis skaičius yra netiesiogiai suderinamas su Skaičius, todėl Sveikas skaičius [] yra netiesiogiai kovojantis su Skaičius []ir Skaičius [] yra aiškiai besivaržantis Sveikas skaičius [] . 3 paveiksle pavaizduotas.

Andreasas Solymosi

Apskritai galime sakyti, kad masyvų tipai yra nevienodi Java. Toliau straipsnyje apžvelgsime bendrinių tipų kovariacijos pavyzdžius.

Kontraversija

Kaip ir kovariacija, prieštaringumas yra a nukreiptas santykiai. Nors kovariacija reiškia su-kitoks, prieštaringumas reiškia prieš-skirtingi. Kaip jau minėjau anksčiau, pavadinimai išreiškia koreliacijos kryptį. Taip pat svarbu pažymėti, kad dispersija nėra tipų, o tik tipų atributas priklausomas tipai (pvz., masyvai ir bendrieji tipai, taip pat metodai, kuriuos aptarsiu 2 dalyje).

Priklausomas tipas, pvz A (T) vadinamas kontravariškas jei suderinamumas T1 į T2 reiškia suderinamumą A (T.2) iki A (T.1). 4 paveiksle pavaizduotas.

Andreasas Solymosi

Kalbos elementas (tipas ar metodas) A (T) priklausomai nuo T yra kovariantas jei suderinamumas T1 į T2 reiškia suderinamumą A (T.1) iki A (T.2). Jei suderinamumas T1 į T2 reiškia suderinamumą A (T.2) iki A (T.1), tada tipas A (T) yra kontravariškas. Jei suderinamumas T1 tarp T2 nereiškia jokio suderinamumo tarp A (T.1) ir A (T.2), tada A (T) yra nekintantis.

Masyvo tipai „Java“ nėra netiesiogiai prieštaringas, bet jie gali būti aiškiai prieštaringas , kaip ir bendrieji tipai. Aš pateiksiu keletą pavyzdžių vėliau straipsnyje.

Nuo tipo priklausantys elementai: metodai ir tipai

„Java“ metodai, masyvo tipai ir bendrieji (parametrizuoti) tipai yra nuo tipo priklausantys elementai. Metodai priklauso nuo jų parametrų tipų. Masyvo tipas, T [], priklauso nuo jo elementų tipų, T. Bendrasis tipas G priklauso nuo jo tipo parametro, T. 5 paveiksle pavaizduotas.

Andreasas Solymosi

Daugiausia šiame straipsnyje pagrindinis dėmesys skiriamas tipų suderinamumui, nors aš paliesiu metodų suderinamumą 2 dalies pabaigoje.

Numanomas ir aiškus tipo suderinamumas

Anksčiau jūs matėte tipą T1 esamas netiesiogiai (arba aiškiai) suderinamas su T2. Tai teisinga tik tada, kai priskiriamas tipo kintamasis T1 į tipo kintamąjį T2 leidžiama be (arba su) žymėjimo. Tipinis perdavimas yra dažniausias būdas pažymėti aiškų suderinamumą:

 variableOfTypeT2 = kintamasOfTypeT1; // numanomas suderinamas kintamasisOfTypeT2 = (T2) kintamasisOfTypeT1; // aiškiai suderinamas 

Pavyzdžiui, tarpt yra netiesiogiai suderinamas su ilgas ir aiškiai suderinama su trumpas:

 int kintamas = 5; ilgas ilgasKintamas = kintamas; // numanomas suderinamas trumpas trumpasis kintamasis = (trumpas) kintamasis; // aiškiai suderinamas 

Numanomas ir aiškus suderinamumas egzistuoja ne tik priskiriant, bet ir perduodant parametrus nuo metodo iškvietimo iki metodo apibrėžimo ir atgal. Kartu su įvesties parametrais tai reiškia ir funkcijos rezultato perdavimą, kurį atliktumėte kaip išvesties parametrą.

Prisimink tai loginis nėra suderinamas su jokiu kitu tipu, taip pat primityvus ir nuorodinis tipas niekada negali būti suderinami.

Metodo parametrai

Mes sakome, kad metodas nuskaito įvesties parametrus ir rašo išvesties parametrus. Primityvių tipų parametrai visada yra įvesties parametrai. Funkcijos grąžinimo vertė visada yra išvesties parametras. Nuorodų tipų parametrai gali būti abu: jei metodas pakeičia nuorodą (arba primityvų parametrą), pakeitimas lieka metodo viduje (tai reiškia, kad po skambučio jis nematomas už metodo ribų - tai vadinama skambinkite pagal vertę). Tačiau jei metodas pakeičia nurodytą objektą, pakeitimas išlieka grąžinus iš metodo - tai vadinama skambinti pagal nuorodą.

(Nuorodos) potipis yra netiesiogiai suderinamas su jo supertipu, o supertipas yra aiškiai suderinamas su jo potipiu. Tai reiškia, kad nuorodų tipai yra suderinami tik pagal jų hierarchijos šaką - netiesiogiai aukštyn ir aiškiai žemyn:

 referenceOfSuperType = referenceOfSubType; // implicit suderinama referenceOfSubType = (SubType) nuorodaOfSuperType; // aiškiai suderinamas 

„Java“ kompiliatorius paprastai leidžia numanomai suderinti užduotį tik jei nėra pavojaus prarasti informaciją vykdymo metu tarp skirtingų tipų. (Tačiau atminkite, kad ši taisyklė negalioja, jei netenkate tikslumo, pvz., Atlikdami užduotį iš tarpt plūduriuoti.) Pavyzdžiui, tarpt yra netiesiogiai suderinamas su ilgas nes a ilgas kintamasis tinka kiekvienam tarpt vertė. Priešingai, a trumpas kintamasis neturi jokio tarpt vertybės; taigi tarp šių elementų leidžiamas tik aiškus suderinamumas.

Andreasas Solymosi

Atkreipkite dėmesį, kad numanomas suderinamumas 6 paveiksle daro prielaidą, kad ryšys yra tranzityvus: trumpas yra suderinamas su ilgas.

Panašiai kaip matote 6 paveiksle, visada galima priskirti potipio nuorodą tarpt supertipo nuoroda. Atminkite, kad ta pati užduotis kita kryptimi gali mesti a „ClassCastException“, tačiau „Java“ kompiliatorius jį leidžia tik perduodant tipą.

Kovariacija ir prieštaringumas masyvo tipams

„Java“ kai kurie masyvų tipai yra nevienodi ir (arba) prieštaringi. Kovariacijos atveju tai reiškia, kad jei T yra suderinamas su Utada T [] taip pat yra suderinamas su U []. Prieštaringumo atveju tai reiškia U [] yra suderinamas su T []. „Java“ nekeičiami primityvių tipų masyvai:

 longArray = intArray; // tipo klaida shortArray = (trumpas []) intArray; // tipo klaida 

Standartinių tipų masyvai yra numanomai kovariantas ir aiškiai prieštaringastačiau:

 „SuperType“] superArray; SubType [] subArray; ... superArray = subArray; // implicit covariant subArray = (SubType []) superArray; // aiškus kontravariantas 
Andreasas Solymosi

Netiesioginis masyvų kovariacija

Praktiškai tai reiškia, kad masyvo komponentų priskyrimas gali mesti „ArrayStoreException“ vykdymo metu. Jei masyvo nuoroda „SuperType“ nuorodos į masyvo objektą Potipis, tada vienas jo komponentas priskiriamas a „SuperType“ tada:

 superArray [1] = naujas „SuperType“); // meta ArrayStoreException 

Tai kartais vadinama kovariacijos problema. Tikroji problema yra ne tiek išimtis (kurios būtų galima išvengti programuojant drausmę), bet tai, kad virtuali mašina turi patikrinti kiekvieną masyvo elemento užduotį vykdymo metu. Tai daro „Java“ efektyvumo požiūriu nepalankesnį nei kalbų be kovariantiškumo (kur draudžiama suderinti masyvo nuorodų priskyrimas) arba tokioms kalboms kaip „Scala“, kur kovariantiją galima išjungti.

Kovariacijos pavyzdys

Paprastame pavyzdyje masyvo nuoroda yra tipo Objektas [] bet masyvo objektas ir elementai yra skirtingų klasių:

 Object [] objectArray; // masyvo nuorodos objektasArray = nauja eilutė [3]; // masyvo objektas; suderinamas priskyrimas objectArray [0] = naujas sveikasis skaičius (5); // meta ArrayStoreException 

Dėl kovariacijos kompiliatorius negali patikrinti paskutinio masyvo elementų priskyrimo teisingumo - JVM tai daro ir už dideles išlaidas. Tačiau kompiliatorius gali optimizuoti išlaidas, jei nenaudojamas tipų suderinamumas tarp masyvo tipų.

Andreasas Solymosi

Atminkite, kad „Java“ naudoti tam tikro tipo kintamajam kintamajam draudžiama nurodyti jo supertipų objektą: rodyklės 8 paveiksle neturi būti nukreiptos į viršų.

Bendrų tipų variacijos ir pakaitos

Bendrieji (parametrizuoti) tipai yra netiesiogiai nekintamas „Java“, o tai reiškia, kad skirtingi bendro tipo pavyzdžiai nėra tarpusavyje suderinami. Net tipinis liejimas nesuderins suderinamumo:

 Generic superGeneric; Generic subGeneric; subGeneric = (Bendras) superGeneric; // tipo klaida superGeneric = (Generic) subGeneric; // tipo klaida 

Tipo klaidos vis dėlto atsiranda subGeneric.getClass () == superGeneric.getClass (). Problema ta, kad metodas „getClass“ () nustato neapdorotą tipą - štai kodėl tipo parametras nepriklauso metodo parašui. Taigi, dviejų metodų deklaracijos

 tuštumos metodas (Generic p); tuštumos metodas (Generic p); 

neturi atsirasti kartu sąsajos (arba abstrakčios klasės) apibrėžime.

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