Stvaranje generičkog niza u Javi

1. Uvod

Možda ćemo željeti koristiti nizove kao dio klasa ili funkcija koje podržavaju generičke. Zbog načina na koji Java postupa s generičkim lijekovima, to može biti teško.

U ovom uputstvu razumjet ćemo izazove korištenja generičkih lijekova s ​​nizovima. Zatim ćemo stvoriti primjer generičkog niza.

Također ćemo pogledati gdje je Java API riješio sličan problem.

2. Razmatranja prilikom upotrebe generičkih nizova

Važna razlika između nizova i generičkih podataka jest način na koji oni provode provjeru tipa. Konkretno, nizovi pohranjuju i provjeravaju informacije o tipu tijekom izvođenja. Međutim, generički podaci provjeravaju greške u tipu tijekom vremena kompajliranja i nemaju podatke o tipu tijekom izvođenja.

Sintaksa Java sugerira da bismo mogli stvoriti novi generički niz:

T [] elementi = novi T [veličina];

Ali, ako bismo to pokušali, dobili bismo pogrešku pri kompajliranju.

Da bismo razumjeli zašto, uzmimo u obzir sljedeće:

javni T [] getArray (int veličina) {T [] genericArray = novi T [veličina]; // pretpostavimo da je ovo dopušteno return genericArray; }

Kao nevezani generički tip T rješava do Objekt, naša metoda u vrijeme izvođenja bit će:

javni objekt [] getArray (int veličina) {objekt [] genericArray = novi objekt [veličina]; return genericArray; }

Zatim, ako pozovemo našu metodu i rezultat pohranimo u Niz niz:

Niz [] myArray = getArray (5);

Kôd će se kompajlirati u redu, ali neće uspjeti tijekom izvođenja s a ClassCastException. To je zato što smo upravo dodijelili Objekt[] do a Niz[] referenca. Konkretno, implicitni odabir kompajlera ne bi uspio pretvoriti Objekt[] našem potrebnom tipu Niz[].

Iako ne možemo izravno inicijalizirati generičke nizove, ipak je moguće postići ekvivalentnu operaciju ako pozivni kod pruža točnu vrstu podataka.

3. Stvaranje generičkog niza

Za naš primjer, razmotrimo ograničenu strukturu podataka steka MyStack, gdje je kapacitet fiksiran na određenu veličinu. Također, kako bismo htjeli da stek radi s bilo kojom vrstom, razuman izbor implementacije bio bi generički niz.

Prvo, stvorimo polje za pohranu elemenata našeg stoga, koji je generički niz vrsta E:

privatni E [] elementi;

Drugo, dodajmo konstruktor:

javni MyStack (klasa klazz, int kapacitet) {elements = (E []) Array.newInstance (klazz, kapacitet); }

Primijetite kako koristimo java.lang.reflect.Array # newInstance za inicijalizaciju našeg generičkog niza, koji zahtijeva dva parametra. Prvi parametar specificira vrstu objekta unutar novog polja. Drugi parametar određuje koliko prostora treba stvoriti za niz. Kao rezultat Niz # newInstance je tipa Objekt, moramo ga baciti na E [] stvoriti naš generički niz.

Također bismo trebali primijetiti konvenciju imenovanja parametra tipa pljesak rađe nego razred, što je rezervirana riječ u Javi.

4. Uzimajući u obzir ArrayList

4.1. Koristeći ArrayList na mjestu niza

Često je lakše koristiti generički ArrayList umjesto generičkog niza. Pogledajmo kako se možemo promijeniti MyStack koristiti an ArrayList.

Prvo, kreirajmo polje za pohranu naših elemenata:

privatni elementi popisa;

Drugo, u našem konstruktoru steka možemo inicijalizirati ArrayList s početnim kapacitetom:

elementi = novi ArrayList (kapacitet);

Čini naš razred jednostavnijim, jer ne moramo koristiti refleksiju. Također, od nas se ne traži da dodamo literal klase prilikom stvaranja našeg stoga. Napokon, kao što možemo postaviti početni kapacitet an ArrayList, možemo dobiti iste pogodnosti kao niz.

Stoga moramo generičke nizove konstruirati samo u rijetkim situacijama ili kada se povezujemo s nekom vanjskom knjižnicom koja zahtijeva niz.

4.2. ArrayList Provedba

Zanimljivo, ArrayList sam se provodi pomoću generičkih nizova. Zavirimo unutra ArrayList da vidim kako.

Prvo, pogledajmo polje elemenata popisa:

prolazni objekt [] elementData;

Obavijest ArrayList koristi Objekt kao tip elementa. Kako naš generički tip nije poznat do vremena izvođenja, Objekt koristi se kao superrazred bilo koje vrste.

Vrijedno je napomenuti da su gotovo sve operacije u ArrayList mogu koristiti ovaj generički niz jer ne trebaju pružiti snažno otkucani niz vanjskom svijetu, osim jedne metode - toArray!

5. Izgradnja niza iz kolekcije

5.1. Primjer povezanog popisa

Pogledajmo korištenje generičkih nizova u API-ju Java Collections, gdje ćemo iz kolekcije izgraditi novi niz.

Prvo, stvorimo novi LinkedList s argumentom tipa Niz i dodajte joj stavke:

Stavke na popisu = novi LinkedList (); items.add ("prva stavka"); items.add ("druga stavka"); 

Drugo, napravimo niz stavki koje smo upravo dodali:

String [] itemsAsArray = items.toArray (novi String [0]);

Da bismo izgradili svoj niz, Popis.toArray metoda zahtijeva ulazni niz. Ovaj niz koristi isključivo za dobivanje informacija o tipu za stvaranje povratnog niza pravog tipa.

U našem gornjem primjeru smo koristili novi niz [0] kao naš ulazni niz za izgradnju rezultirajućeg Niz niz.

5.2. LinkedList.toArray Provedba

Zavirimo unutra LinkedList.toArray, da vidimo kako je implementiran u Java JDK.

Prvo, pogledajmo potpis metode:

javni T [] doArray (T [] a)

Drugo, pogledajmo kako se stvara novi niz kada je potrebno:

a = (T []) java.lang.reflect.Array.newInstance (a.getClass (). getComponentType (), veličina);

Primijetite kako koristi Niz # newInstance za izgradnju novog niza, kao u našem primjeru steka ranije. Također, primijetite kako parametar a koristi se za pružanje tipa Niz # newInstance. Napokon, rezultat iz Niz # newInstance bačena je na T [] stvoriti generički niz.

6. Zaključak

U ovom smo članku prvo pogledali razlike između nizova i generika, a zatim je slijedio primjer stvaranja generičkog niza. Zatim smo pokazali kako se pomoću ArrayList može biti jednostavnije nego koristiti generički niz. Konačno, pogledali smo i upotrebu generičkog niza u API-ju Collections.

Kao i uvijek, primjer koda dostupan je na GitHub-u.