Programovanie

Typ závislosti v Jave, časť 1

Pochopenie kompatibility typov je pre písanie dobrých programov Java zásadné, ale vzájomné pôsobenie odchýlok medzi prvkami jazyka Java môže pre nezasvätených pôsobiť vysoko akademicky. Tento článok je určený pre vývojárov softvéru, ktorí sú pripravení zvládnuť túto výzvu! 1. časť odhaľuje kovariantné a kontrariantné vzťahy medzi jednoduchšími prvkami, ako sú typy polí a generické typy, ako aj špeciálny element jazyka Java, zástupný znak. Časť 2 skúma závislosť a rozptyl typov v bežných príkladoch API a vo výrazoch lambda.

download Stiahnite si zdroj Získajte zdrojový kód pre tento článok „Závislosť typu v jazyku Java, časť 1.“ Vytvoril pre JavaWorld Dr. Andreas Solymosi.

Pojmy a terminológia

Predtým, ako sa dostaneme do vzťahov kovariancie a kontravarencie medzi rôznymi prvkami jazyka Java, uistite sa, že máme spoločný koncepčný rámec.

Kompatibilita

V objektovo orientovanom programovaní kompatibilita sa vzťahuje na riadený vzťah medzi typmi, ako je znázornené na obrázku 1.

Andreas Solymosi

Hovoríme, že dva typy sú kompatibilný v Jave, ak je možné prenášať údaje medzi premennými typov. Prenos dát je možný, ak to kompilátor akceptuje, a je realizovaný priradením alebo odovzdaním parametra. Ako príklad, krátky je kompatibilný s int pretože zadanie intVariable = shortVariable; je možné. ale boolean nie je kompatibilný s int pretože zadanie intVariable = booleanVariable; nie je možné; kompilátor to neprijme.

Pretože kompatibilita je niekedy riadený vzťah T1 je kompatibilný s T2 ale T2 nie je kompatibilný s T1, alebo nie rovnakým spôsobom. Uvidíme to ďalej, keď sa dostaneme k diskusii o explicitnej alebo implicitnej kompatibilite.

Dôležité je, že je možná kompatibilita medzi referenčnými typmi iba v rámci hierarchie typov. Všetky typy tried sú kompatibilné s Objektnapríklad preto, že všetky triedy dedia implicitne z Objekt. Celé číslo nie je kompatibilný s Plavák, však preto Plavák nie je nadtrieda Celé číslo. Celé čísloje kompatibilný s Číslo, pretože Číslo je (abstraktná) nadtrieda Celé číslo. Pretože sa nachádzajú v hierarchii rovnakého typu, kompilátor prijme priradenie numberReference = integerReference;.

Hovoríme o implicitné alebo výslovne kompatibilita, v závislosti od toho, či musí byť kompatibilita výslovne označená alebo nie. Napríklad krátke je implicitne kompatibilný s int (ako je uvedené vyššie), ale nie naopak: zadanie shortVariable = intVariable; nie je možné. Krátka je však výslovne kompatibilný s int, pretože zadanie shortVariable = (short) intVariable; je možné. Tu musíme označiť kompatibilitu pomocou odlievanie, tiež známy ako prevod typu.

Podobne medzi referenčnými typmi: integerReference = numberReference; je neprijateľné, iba integerReference = (Integer) numberReference; bude prijatý. Preto Celé číslo je implicitne kompatibilný s Číslo ale Číslo je len výslovne kompatibilný s Celé číslo.

Závislosť

Typ môže závisieť od iných typov. Napríklad typ poľa int [] záleží na primitívnom type int. Podobne aj generický typ ArrayList je závislá od typu Zákazník. Metódy môžu byť tiež závislé od typu, v závislosti od typov ich parametrov. Napríklad metóda prírastok void (celé číslo i); záleží od typu Celé číslo. Niektoré metódy (napríklad niektoré všeobecné typy) závisia od viac ako jedného typu - napríklad metódy s viac ako jedným parametrom.

Kovariancia a kontravarencia

Kovariancia a kontrarariance určujú kompatibilitu na základe typov. V obidvoch prípadoch je variancia priamym vzťahom. Kovariancia možno preložiť ako „odlišné v rovnakom smere“, alebo s-rôznymi, keďže protirečivosť znamená „odlišné v opačnom smere“, alebo proti-rôzne. Kovovariantné a kontrariantné typy nie sú rovnaké, ale existuje medzi nimi korelácia. Názvy naznačujú smer korelácie.

Takže kovariancia znamená, že kompatibilita dvoch typov znamená kompatibilitu typov na nich závislých. Vzhľadom na kompatibilitu typov sa predpokladá, že závislé typy sú kovariantné, ako je znázornené na obrázku 2.

Andreas Solymosi

Zlučiteľnosť T1 do T2 znamená kompatibilitu A (T.1) až A (T.2). Závislý typ A (T) sa volá kovarianty; alebo presnejšie A (T.1) je kovariančné s A (T.2).

Pre ďalší príklad: pretože zadanie numberArray = integerArray; je možné (minimálne v Jave) typy polí Celé číslo [] a Číslo [] sú kovariantné. Môžeme to teda povedať Celé číslo [] je implicitne kovariantný do Číslo []. A hoci opak nie je pravdou - zadanie integerArray = numberArray; nie je možné - zadanie s odlievaním typu (integerArray = (Integer []) numberArray;) je možné; preto hovoríme: Číslo [] je výslovne kovariančné do Celé číslo [] .

Zhrnúť: Celé číslo je implicitne kompatibilný s Číslo, preto Celé číslo [] je implicitne kovariantný k Číslo []a Číslo [] je výslovne kovariančný s Celé číslo [] . Obrázok 3 ilustruje.

Andreas Solymosi

Všeobecne možno povedať, že typy polí sú v Jave kovariantné. Na príklady kovariancie medzi generickými typmi sa pozrieme ďalej v článku.

Rozporuplnosť

Rovnako ako kovariancia, aj kontravariancia je smeroval vzťah. Zatiaľ čo kovariancia znamená s-rôznymi, rozpor znamená proti-rôzne. Ako som už spomenul, názvy vyjadrujú smer korelácie. Je tiež dôležité poznamenať, že variancia nie je atribútom typov všeobecne, ale iba typu závislý typy (napríklad polia a generické typy a tiež metódy, ktorým sa budem venovať v 2. časti).

Závislý typ ako napr A (T) sa volá protikladný ak je kompatibilita T1 do T2 znamená kompatibilitu A (T.2) až A (T.1). Obrázok 4 ilustruje.

Andreas Solymosi

Jazykový prvok (typ alebo metóda) A (T) záleží na T je kovarianty ak je kompatibilita T1 do T2 znamená kompatibilitu A (T.1) až A (T.2). Ak je kompatibilita T1 do T2 znamená kompatibilitu A (T.2) až A (T.1), potom typ A (T) je protikladný. Ak je kompatibilita T1 medzi T2 neznamená žiadnu kompatibilitu medzi A (T.1) a A (T.2), potom A (T) je nemenný.

Typy polí v Jave nie sú implicitne v rozpore, ale môžu byť výslovne proti , rovnako ako všeobecné typy. Ďalej uvediem niekoľko príkladov v článku.

Prvky závislé od typu: Metódy a typy

V Jave sú metódy, typy polí a všeobecné (parametrizované) typy typovo závislé prvky. Metódy závisia od typu ich parametrov. Typ poľa, T [], je závislá na druhoch jej prvkov, T. Všeobecný typ G je závislá na jeho parametri typu, T. Obrázok 5 zobrazuje.

Andreas Solymosi

Tento článok sa väčšinou zameriava na kompatibilitu typov, aj keď sa budem na konci 2. časti zaoberať kompatibilitou medzi metódami.

Implicitná a explicitná kompatibilita typov

Skôr ste videli typ T1 bytie implicitne (alebo výslovne) kompatibilný s T2. To platí iba v prípade, že ide o priradenie premennej typu T1 na premennú typu T2 je povolený bez označenia (alebo s). Cast casting je najbežnejším spôsobom označenia explicitnej kompatibility:

 variableOfTypeT2 = variableOfTypeT1; // implicitná kompatibilná variableOfTypeT2 = (T2) variableOfTypeT1; // explicitne kompatibilný 

Napríklad, int je implicitne kompatibilný s dlho a výslovne kompatibilný s krátky:

 int intVariable = 5; long longVariable = intVariable; // implicitne kompatibilny short shortVariable = (short) intVariable; // explicitne kompatibilný 

Implicitná a explicitná kompatibilita existuje nielen v priradeniach, ale aj v odovzdávaní parametrov z volania metódy do definície metódy a späť. Spolu so vstupnými parametrami to znamená aj odovzdanie výsledku funkcie, ktorý by ste vykonali ako výstupný parameter.

Poznač si to boolean nie je kompatibilný s iným typom, ani primitívny a referenčný typ nemôže byť kompatibilný.

Parametre metódy

Hovoríme, že metóda číta vstupné parametre a zapisuje výstupné parametre. Parametre primitívnych typov sú vždy vstupné parametre. Návratová hodnota funkcie je vždy výstupným parametrom. Parametre typov odkazov môžu byť obidva: ak metóda zmení referenciu (alebo primitívny parameter), zmena zostane v metóde (čo znamená, že po ukončení hovoru nie je viditeľná mimo metódy - toto sa označuje ako volať podľa hodnoty). Ak metóda zmení sprostredkovaný objekt, zmena zostáva po vrátení z metódy - nazýva sa to volať referenciou.

(Referenčný) podtyp je implicitne kompatibilný s jeho nadtypom a supertyp je výslovne kompatibilný s jeho podtypom. To znamená, že referenčné typy sú kompatibilné iba v rámci ich hierarchickej vetvy - smerom nahor implicitne a smerom nadol explicitne:

 referenceOfSuperType = referenceOfSubType; // implicitný kompatibilný referenceOfSubType = (SubType) referenceOfSuperType; // explicitne kompatibilný 

Kompilátor Java zvyčajne umožňuje implicitnú kompatibilitu priradenia iba ak neexistuje nebezpečenstvo straty informácií za behu medzi rôznymi typmi. (Upozorňujeme však, že toto pravidlo nie je platné pre stratu presnosti, napríklad pri zadaní z int plávať.) Napríklad int je implicitne kompatibilný s dlho pretože a dlho premenná drží každý int hodnotu. Naproti tomu a krátky premenná nedrží žiadnu int hodnoty; medzi týmito prvkami je teda povolená iba výslovná kompatibilita.

Andreas Solymosi

Upozorňujeme, že implicitná kompatibilita na obrázku 6 predpokladá vzťah tranzitívny: krátky je kompatibilný s dlho.

Podobne ako na obrázku 6, je vždy možné priradiť odkaz na podtyp int odkaz na supertyp. Majte na pamäti, že rovnaké zadanie v opačnom smere by mohlo spôsobiť a ClassCastException, avšak kompilátor Java to umožňuje iba s castingom typu.

Kovariancia a kontrariance pre typy polí

V Jave sú niektoré typy polí kovariantné a / alebo kontrariantné. V prípade kovariancie to znamená, že ak T je kompatibilný s Upotom T [] je tiež kompatibilný s U []. V prípade rozporu to znamená U [] je kompatibilný s T []. Polia primitívnych typov sú v Jave invariantné:

 longArray = intArray; // chyba typu shortArray = (short []) intArray; // chyba typu 

Polia referenčných typov sú implicitne kovariantný a výslovne proti, avšak:

 SuperType [] superArray; SubType [] subArray; ... superArray = subArray; // implicitný kovariant subArray = (SubType []) superArray; // výslovný kontrariant 
Andreas Solymosi

Obrázok 7. Implicitná kovariancia pre polia

To prakticky znamená, že priradenie komponentov poľa môže spôsobiť ArrayStoreException za behu. Ak je odkaz na pole z Supertyp odkazuje na objekt poľa z Podtyp, a jeden z jeho komponentov je potom priradený k a Supertyp objekt, potom:

 superArray [1] = nový SuperType (); // hodí ArrayStoreException 

Toto sa niekedy nazýva kovariančný problém. Skutočným problémom nie je ani tak výnimka (ktorej by sa dalo vyhnúť pri disciplíne programovania), ale to, že virtuálny stroj musí za behu skontrolovať každé priradenie v prvku poľa. Toto vystavuje Javu nevýhode efektívnosti oproti jazykom bez kovariancie (kde je zakázané kompatibilné priradenie odkazov na pole) alebo jazykom ako Scala, kde je možné kovarianciu vypnúť.

Príklad pre kovarianciu

V jednoduchom príklade je odkaz na pole typu Objekt [] ale objekt poľa a prvky majú rôzne triedy:

 Object [] objectArray; // referencia poľa objectArray = nový reťazec [3]; // objekt poľa; kompatibilné priradenie objectArray [0] = nové celé číslo (5); // hodí ArrayStoreException 

Z dôvodu kovariancie kompilátor nemôže skontrolovať správnosť posledného priradenia k prvkom poľa - JVM to robí a za značné náklady. Kompilátor však môže náklady optimalizovať, ak sa medzi typmi polí nepoužíva kompatibilita typov.

Andreas Solymosi

Pamätajte, že v Jave je pre referenčnú premennú určitého typu zakázané odkazovať na objekt jej supertypu: šípky na obrázku 8 nesmú smerovať nahor.

Odchýlky a zástupné znaky vo všeobecných typoch

Všeobecné (parametrizované) typy sú implicitne nemenný v Jave, čo znamená, že rôzne inštancie generického typu nie sú navzájom kompatibilné. Kompatibilita nebude mať za následok ani obsadenie typu:

 Generický superGenerický; Generické subGenerické; subGeneric = (Generic) superGeneric; // chyba typu superGeneric = (Generic) subGeneric; // chyba typu 

Typové chyby vznikajú, aj keď subGeneric.getClass () == superGeneric.getClass (). Problém je v tom, že metóda getClass () určuje prvotný typ - to je dôvod, prečo parameter typu nepatrí k podpisu metódy. Teda dve deklarácie metód

 metóda prázdnoty (generická p); metóda prázdnoty (generická p); 

sa nesmú vyskytovať spoločne v definícii rozhrania (alebo abstraktnej triedy).

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