Programiranje

Odvisnost od tipa v Javi, 1. del

Razumevanje združljivosti tipov je bistvenega pomena za pisanje dobrih programov Java, toda medsebojno vplivanje na razlike med elementi jezika Java se lahko nepoznavalcem zdi zelo akademsko. Ta članek je namenjen razvijalcem programske opreme, ki so pripravljeni spoprijeti se z izzivom! Prvi del razkriva kovariante in kontravariantne odnose med enostavnejšimi elementi, kot so tipi matrike in generični tipi, pa tudi posebni element jezika Java, nadomestni znak. Drugi del raziskuje odvisnost in varianto tipa v pogostih primerih API in v lambda izrazih.

prenos Prenos vira Pridobite izvorno kodo za ta članek, "Odvisnost od tipa v Javi, 1. del". Za JavaWorld ustvaril dr. Andreas Solymosi.

Pojmi in terminologija

Preden se lotimo odnosov med kovarianco in kontravariancem med različnimi elementi jezika Java, se prepričajmo, da imamo skupen konceptualni okvir.

Kompatibilnost

Pri objektno usmerjenem programiranju kompatibilnost se nanaša na usmerjeno razmerje med vrstami, kot je prikazano na sliki 1.

Andreas Solymosi

Pravimo, da sta dve vrsti združljiv v Javi, če je mogoče prenos podatkov med spremenljivkami vrst. Prenos podatkov je mogoč, če ga prevajalnik sprejme in se opravi z dodeljevanjem ali posredovanjem parametrov. Kot primer lahko kratek je združljiv z int ker naloga intVariable = shortVariable; mogoče. Ampak logično ni združljiv z int ker naloga intVariable = booleanVariable; ni mogoče; prevajalnik tega ne bo sprejel.

Ker je združljivost včasih usmerjen odnos T1 je združljiv z T2 ampak T2 ni združljiv z T1, ali ne na enak način. To bomo videli še naprej, ko bomo razpravljali o eksplicitni ali implicitni združljivosti.

Pomembno je, da je možna združljivost med referenčnimi vrstami samo znotraj hierarhije tipov. Vsi tipi razredov so združljivi z Predmetna primer zato, ker vsi razredi implicitno podedujejo od Predmet. Celo število ni združljiv z Floatpa zato Float ni superrazred Celo število. Celo številoje združljiv z Številka, Ker Številka je (abstraktni) superrazred Celo število. Ker se nahajajo v isti tipični hierarhiji, prevajalnik sprejme nalogo numberReference = integerReference;.

Govorimo o implicitno ali izrecno združljivost, odvisno od tega, ali mora biti združljivost označena izrecno ali ne. Na primer, kratka je implicitno združljiv z int (kot je prikazano zgoraj), ne pa tudi obratno: naloga shortVariable = intVariable; ni mogoča. Vendar kratek je izrecno združljiv z int, ker je naloga shortVariable = (kratko) intVariable; mogoče. Tu moramo združljivost označiti z litje, znano tudi kot pretvorba tipa.

Podobno med referenčnimi vrstami: integerReference = numberReference; ni sprejemljivo, samo integerReference = (Integer) numberReference; bi bila sprejeta. Zato Celo število je implicitno združljiv z Številka ampak Številka je samo izrecno združljiv z Celo število.

Odvisnost

Tip je lahko odvisen od drugih vrst. Na primer vrsta matrike int [] odvisno od primitivnega tipa int. Podobno tudi generični tip ArrayList je odvisno od vrste Stranka. Metode so lahko odvisne tudi od tipa, odvisno od vrste njihovih parametrov. Na primer metoda prirastek praznine (celo število i); odvisno od vrste Celo število. Nekatere metode (na primer nekatere splošne vrste) so odvisne od več vrst - na primer metode z več kot enim parametrom.

Kovarianca in kontravarianca

Kovarianca in kontravarianca določata združljivost glede na vrste. V obeh primerih je varianca usmerjena relacija. Kovarianca lahko prevedemo kot "različni v isti smeri," oz s-drugačno, medtem ko kontravarenca pomeni "drugače v nasprotni smeri", oz proti-drugačnim. Kovariantni in kontravariantni vrsti nista enaki, vendar obstaja povezava med njima. Imena kažejo smer korelacije.

Torej, kovarianca pomeni, da združljivost dveh vrst pomeni združljivost vrst, ki so odvisne od njih. Glede na združljivost tipov predpostavljamo, da so odvisni tipi kovarianti, kot je prikazano na sliki 2.

Andreas Solymosi

Združljivost T1 do T2 pomeni združljivost A (T1) do A (T2). Odvisni tip A (T) je poklican kovariantno; ali natančneje, A (T1) je kovarijantna na A (T2).

Za drug primer: ker je naloga numberArray = integerArray; (vsaj v Javi) vrste matrike Celo število [] in Številka [] so kovariante. Torej, to lahko rečemo Celo število [] je implicitno kovarianten do Številka []. In čeprav nasprotno ne drži - naloga integerArray = numberArray; ni mogoče - dodelitev s tipskim litjem (integerArray = (Celo število []) numberArray;) je mogoče; zato pravimo, Številka [] je izrecno kovarianten do Celo število [] .

Povzeti: Celo število je implicitno združljiv z Številka, torej Celo število [] je implicitno kovarianten na Številka [], in Številka [] je izrecno kovarijantna na Celo število [] . Slika 3 prikazuje.

Andreas Solymosi

Na splošno lahko rečemo, da so vrste nizov v Java kovariante. V nadaljevanju članka si bomo ogledali primere kovariancije med generičnimi vrstami.

Kontravirnost

Tako kot kovarianca je tudi kontravarianca a usmerjeno razmerje. Medtem ko kovarianca pomeni s-drugačno, kontravariance pomeni proti-drugačnim. Kot sem že omenil, imena izražajo smer korelacije. Pomembno je tudi opozoriti, da varianca na splošno ni atribut tipov, temveč le odvisna vrste (kot so nizi in generični tipi, pa tudi metode, ki jih bom obravnaval v 2. delu).

Odvisen tip, kot je A (T) je poklican kontravariantno če je združljivost T1 do T2 pomeni združljivost A (T2) do A (T1). Slika 4 prikazuje.

Andreas Solymosi

Jezikovni element (vrsta ali metoda) A (T) odvisno od T je kovariantno če je združljivost T1 do T2 pomeni združljivost A (T1) do A (T2). Če je združljivost T1 do T2 pomeni združljivost A (T2) do A (T1), nato tip A (T) je kontravariantno. Če je združljivost T1 med T2 ne pomeni nobene združljivosti med A (T1) in A (T2), potem A (T) je nespremenljivo.

Vrste matrike v Javi niso implicitno kontravariantno, vendar so lahko izrecno kontravariantno , tako kot generične vrste. V nadaljevanju članka bom ponudil nekaj primerov.

Elementi, odvisni od tipa: metode in tipi

V Javi so metode, tipi matrike in splošni (parametrizirani) tipi od tipa odvisni elementi. Metode so odvisne od vrste njihovih parametrov. Vrsta matrike, T [], je odvisno od vrste njegovih elementov, T. Splošni tip G je odvisen od njegovega parametra tipa, T. Slika 5 prikazuje.

Andreas Solymosi

Ta članek se večinoma osredotoča na združljivost vrst, čeprav se bom dotaknil združljivosti med metodami proti koncu 2. dela.

Implicitna in eksplicitna združljivost tipa

Prej ste videli tip T1 biti implicitno (ali izrecno) združljiv z T2. To velja le, če je dodeljena spremenljivka tipa T1 na spremenljivko tipa T2 je dovoljeno brez (ali z) označevanjem. Predvajanje tipov je najpogostejši način označevanja eksplicitne združljivosti:

 variableOfTypeT2 = variableOfTypeT1; // implicitno združljiva variableOfTypeT2 = (T2) variableOfTypeT1; // izrecno združljivo 

Na primer, int je implicitno združljiv z dolga in izrecno združljiv z kratek:

 int intVariable = 5; long longVariable = intVariable; // implicitno združljiv kratki shortVariable = (kratek) intVariable; // izrecno združljivo 

Implicitna in eksplicitna združljivost obstaja ne le pri dodelitvah, temveč tudi pri prenosu parametrov iz klica metode v definicijo metode in nazaj. Skupaj z vhodnimi parametri to pomeni tudi posredovanje rezultata funkcije, kar bi naredili kot izhodni parameter.

Upoštevajte to logično ni združljiv z nobeno drugo vrsto, prav tako pa tudi primitivna in referenčna vrsta ne moreta biti združljivi.

Parametri metode

Pravimo, da metoda bere vhodne parametre in zapisuje izhodne parametre. Parametri primitivnih tipov so vedno vhodni parametri. Vrnjena vrednost funkcije je vedno izhodni parameter. Parametra referenčnih vrst sta lahko oba: če metoda spremeni referenco (ali primitivni parameter), sprememba ostane znotraj metode (kar pomeni, da po klicu ni vidna zunaj metode - to je znano kot klic po vrednosti). Če metoda spremeni navedeni objekt, pa sprememba ostane po vrnitvi iz metode - to je znano kot klic po referenci.

(Referenčni) podtip je implicitno združljiv s svojim nadtipom, nadtip pa izrecno združljiv s svojim podtipom. To pomeni, da so referenčni tipi združljivi samo znotraj svoje veje hierarhije - implicitno navzgor in eksplicitno navzdol:

 referenceOfSuperType = referenceOfSubType; // implicitno združljiv referenceOfSubType = (SubType) referenceOfSuperType; // izrecno združljivo 

Prevajalnik Java običajno dovoljuje implicitno združljivost naloge samo če ni nevarnosti, da bi med izvajanjem med različnimi vrstami izgubili podatke. (Upoštevajte pa, da to pravilo ne velja za izgubo natančnosti, na primer pri nalogi iz int plavati.) Na primer, int je implicitno združljiv z dolga ker a dolga spremenljivka vsebuje vsak int vrednost. Nasprotno pa a kratek spremenljivka ne vsebuje nobene int vrednote; tako je med temi elementi dovoljena le izrecna združljivost.

Andreas Solymosi

Upoštevajte, da implicitna združljivost na sliki 6 predpostavlja, da je razmerje prehodno: kratek je združljiv z dolga.

Podobno kot vidite na sliki 6, je vedno mogoče dodeliti sklic na podtip int sklic na nadtip. Upoštevajte, da bi lahko enaka naloga v drugo smer vrgla a ClassCastExceptionvendar ga prevajalnik Java dovoljuje le z ulivanjem vrst.

Kovarianca in kontravariance za vrste matrike

V Javi so nekatere vrste nizov kovarijantne in / ali kontravariantne. V primeru kovariacije to pomeni, da če T je združljiv z U, potem T [] je tudi združljiv z U []. V primeru kontravariance to pomeni U [] je združljiv z T []. Nizi primitivnih vrst so v Java invariantni:

 longArray = intArray; // napaka tipa shortArray = (kratko []) intArray; // napaka tipa 

Nizov referenčnih vrst je implicitno kovarianten in izrecno kontravariantno, vendar:

 SuperType [] superArray; SubType [] subArray; ... superArray = subArray; // implicitni kovariantni podArray = (SubType []) superArray; // eksplicitna kontravarianta 
Andreas Solymosi

Slika 7. Implicitna kovarianca za nize

To v praksi pomeni, da lahko dodelitev komponent matrike vrže ArrayStoreException med izvajanjem. Če je sklic na matriko SuperType se sklicuje na objekt matrike Podtipin ena od njegovih komponent se nato dodeli a SuperType predmet, nato:

 superArray [1] = nov SuperType (); // vrže ArrayStoreException 

Temu včasih rečejo problem kovariacije. Resnična težava ni toliko izjema (ki bi se ji lahko izognili s programiranjem), temveč to, da mora navidezni stroj med izvajanjem preveriti vsako dodelitev v elementu polja. To postavlja Javo v slabši izkoristek glede na jezike brez kovariancije (kjer je prepovedana združljiva dodelitev referenc na nize) ali jezike, kot je Scala, kjer je kovarianco mogoče izklopiti.

Primer za kovarianco

V preprostem primeru je sklic na vrsto tipa Predmet [] vendar so objekt matrike in elementi različnih razredov:

 Object [] objectArray; // sklic na matriko objectArray = nov niz [3]; // objekt matrike; združljiva dodelitev objectArray [0] = novo celo število (5); // vrže ArrayStoreException 

Zaradi kovariacije prevajalnik ne more preveriti pravilnosti zadnje dodelitve elementom matrike - JVM to naredi in to z velikimi stroški. Vendar lahko prevajalnik optimizira stroške, če med vrstami matrike ni uporabljena združljivost tipov.

Andreas Solymosi

Ne pozabite, da je v Javi za referenčno spremenljivko neke vrste, ki se sklicuje na objekt svojega nadtipa, prepovedano: puščice na sliki 8 ne smejo biti usmerjene navzgor.

Razlike in nadomestni znaki v generičnih vrstah

Splošni (parametrizirani) tipi so implicitno invariantno v Javi, kar pomeni, da različni primerki generičnega tipa med seboj niso združljivi. Tudi ulivanje vrst ne bo prineslo združljivosti:

 Generični superGeneric; Generični podGeneric; subGeneric = (Generic) superGeneric; // napaka tipa superGeneric = (Generic) subGeneric; // napaka tipa 

Napake tipa se pojavijo, čeprav subGeneric.getClass () == superGeneric.getClass (). Težava je v tem, da metoda getClass () določa neobdelani tip - zato parameter tipa ne spada v podpis metode. Tako dve deklaraciji metode

 metoda praznine (generični p); metoda praznine (generični p); 

ne smejo nastopati skupaj v definiciji vmesnika (ali abstraktnega razreda).

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