Vytvorenie generického poľa v prostredí Java

1. Úvod

Možno by sme chceli použiť polia ako súčasť tried alebo funkcií, ktoré podporujú všeobecné informácie. Vzhľadom na spôsob, akým Java pracuje s generikami, to môže byť ťažké.

V tomto tutoriáli pochopíme výzvy spojené s používaním generík v poliach. Potom vytvoríme príklad všeobecného poľa.

Pozrime sa tiež na to, kde podobný problém vyriešilo rozhranie Java API.

2. Úvahy pri používaní generických polí

Dôležitým rozdielom medzi poľami a generikami je spôsob, akým vynucujú kontrolu typu. Polia konkrétne ukladajú a kontrolujú informácie o type počas behu. Generici však kontrolujú chyby typu v čase kompilácie a za behu nemajú informácie o type.

Syntax jazyka Java naznačuje, že by sme mohli byť schopní vytvoriť nové generické pole:

T [] prvky = nový T [veľkosť];

Keby sme sa o to pokúsili, dostali by sme chybu kompilácie.

Aby sme pochopili prečo, zvážme nasledujúce:

public T [] getArray (int size) {T [] genericArray = new T [size]; // predpokladajme, že je to povolené return genericArray; }

Ako neviazaný všeobecný typ T predsavzal si Objekt, naša metóda za behu bude:

public Object [] getArray (int size) {Object [] genericArray = new Object [size]; návrat genericArray; }

Potom, ak zavoláme našu metódu a výsledok uložíme do a String pole:

Reťazec [] myArray = getArray (5);

Kód bude v poriadku, ale za behu zlyhá s ClassCastException. Je to preto, že sme práve pridelili znak Objekt [] do a Reťazec [] odkaz. Konkrétne implicitné obsadenie kompilátorom by sa nepodarilo previesť Objekt [] na náš požadovaný typ Reťazec [].

Aj keď nemôžeme inicializovať generické polia priamo, stále je možné dosiahnuť ekvivalentnú operáciu, ak volací kód poskytuje presný typ informácie.

3. Vytvorenie generického poľa

Pre náš príklad uvažujme obmedzenú štruktúru dát zásobníka MyStack, kde je kapacita stanovená na určitú veľkosť. Pretože by sme chceli, aby zásobník pracoval s akýmkoľvek typom, rozumnou voľbou implementácie by bolo všeobecné pole.

Najskôr vytvorme pole na uloženie prvkov nášho zásobníka, čo je všeobecné pole typu E:

súkromné ​​prvky E [];

Po druhé, pridajme konštruktor:

public MyStack (Class clazz, int kapacita) {elements = (E []) Array.newInstance (clazz, kapacita); }

Všimnite si ako používame java.lang.reflect.Array # newInstance inicializovať naše generické pole, ktorá vyžaduje dva parametre. Prvý parameter určuje typ objektu vo vnútri nového poľa. Druhý parameter určuje, koľko miesta sa má vytvoriť pre pole. Ako výsledok Pole # newInstance je typu Objekt, musíme to obsadiť E [] aby sme vytvorili naše všeobecné pole.

Mali by sme tiež poznamenať konvenciu pomenovania parametra typu clazz radšej než trieda, čo je v Jave vyhradené slovo.

4. Zvažovanie ArrayList

4.1. Použitím ArrayList na mieste poľa

Často je jednoduchšie použiť generikum ArrayList namiesto generického poľa. Pozrime sa, ako sa môžeme zmeniť MyStack používať ArrayList.

Najskôr vytvorme pole na uloženie našich prvkov:

prvky súkromného zoznamu;

Po druhé, v našom konštruktore zásobníka môžeme inicializovať ArrayList s počiatočnou kapacitou:

elements = new ArrayList (kapacita);

Uľahčuje to našu triedu, pretože nemusíme používať reflexiu. Pri vytváraní nášho zásobníka tiež nie sme povinní odovzdávať triedny literál. Nakoniec, ako môžeme nastaviť počiatočnú kapacitu ArrayList, môžeme získať rovnaké výhody ako pole.

Preto musíme zostrojiť pole generík iba vo výnimočných situáciách alebo keď pracujeme s nejakou externou knižnicou, ktorá vyžaduje pole.

4.2. ArrayList Implementácia

Je zaujímavé, že ArrayList sa implementuje pomocou generických polí. Nakukneme dovnútra ArrayList aby som videl ako.

Najprv sa pozrime na pole prvkov zoznamu:

prechodný Object [] elementData;

Všimnite si ArrayList používa Objekt ako typ prvku. Pretože náš generický typ nie je známy až do spustenia, Objekt sa používa ako nadtrieda ľubovoľného typu.

Stojí za zmienku, že takmer všetky operácie v ArrayList môžu používať toto všeobecné pole, pretože nepotrebujú poskytovať pole silného typu vonkajšiemu svetu, okrem jednej metódy - toArray!

5. Budovanie poľa zo zbierky

5.1. Príklad LinkedList

Pozrime sa na použitie generických polí v rozhraní Java Collections API, kde vytvoríme nové pole zo zbierky.

Najskôr vytvorme nový LinkedList s argumentom typu String a pridať do nej položky:

Zoznam položiek = nový LinkedList (); items.add ("prvá položka"); items.add ("druhá položka"); 

Po druhé, vytvorme pole položiek, ktoré sme práve pridali:

String [] itemsAsArray = items.toArray (nový reťazec [0]);

Aby sme vytvorili naše pole, Zoznam.toArray metóda vyžaduje vstupné pole. Používa toto pole čisto na získanie informácií o type na vytvorenie návratového poľa správneho typu.

V našom príklade vyššie sme použili nový reťazec [0] ako naše vstupné pole na zostavenie výsledného String pole.

5.2. LinkedList.toArray Implementácia

Nahliadneme dovnútra LinkedList.toArray, aby ste zistili, ako je implementovaná v prostredí Java JDK.

Najprv sa pozrime na podpis metódy:

verejné T [] toArray (T [] a)

Po druhé, pozrime sa, ako sa podľa potreby vytvorí nové pole:

a = (T []) java.lang.reflect.Array.newInstance (a.getClass (). getComponentType (), veľkosť);

Všimnite si, ako to využíva Pole # newInstance vytvoriť nové pole, ako v našom príklade zásobníka skôr. Všimnite si tiež, ako parameter a sa používa na poskytnutie typu Pole # newInstance. Nakoniec výsledok z Pole # newInstance je obsadený do T [] vytvoriť všeobecné pole.

6. Záver

V tomto článku sme sa najskôr pozreli na rozdiely medzi poliami a generikami, potom nasledoval príklad vytvorenia generického poľa. Potom sme si ukázali, ako sa používa ArrayList môže byť jednoduchšie ako použitie generického poľa. Nakoniec sme sa tiež pozreli na použitie generického poľa v API Collection.

Vzorový kód je ako vždy k dispozícii na GitHub.