„Java Generics“ pamoka - kas yra „Generics“ ir kaip jas naudoti?

„Java Generics“ yra viena iš svarbiausių „Java“ kalbos ypatybių. Generikų idėja yra gana paprasta, tačiau kartais ji tampa tokia sudėtinga, nes pereinama nuo įprastos sintaksės, susijusios su ja.

Šios pamokos tikslas - lengvai suprantamai supažindinti jus su šia naudinga generikų koncepcija.

Tačiau prieš pasinerdami į pačius generinius vaistus, išsiaiškinkime, kodėl pirmiausia reikėjo „Java“ generinių vaistų.




„Java Generics“ tikslas

Prieš įvedant generinius „Java 5“, galite parašyti ir sukompiliuoti tokį kodo fragmentą, neišmesdami klaidos ar įspėjimo:

List list = new ArrayList(); list.add('hey'); list.add(new Object());

Į sąrašą ar kitą „Java“ kolekciją galite įtraukti bet kokio tipo vertes, nedeklaruodami, kokio tipo duomenis ji saugo. Bet kai nuskaitote reikšmes iš sąrašo, turite aiškiai jas perduoti į tam tikrą tipą.


Apsvarstykite galimybę kartoti anksčiau pateiktą sąrašą.

for (int i=0; i< list.size(); i++) {
String value = (String) list.get(i); //CastClassException when i=1 }

Leidžiant sukurti sąrašą iš anksto nedeklaravus saugomo duomenų tipo, kaip tai darėme, programuotojai gali padaryti klaidų, panašių į viršų, kurios meta ClassCastExceptions vykdymo metu.

Kad programuotojai nedarytų tokių klaidų, buvo įvesti generiniai vaistai.

Naudodami generinius, galite aiškiai nurodyti duomenų tipą, kuris bus saugomas kuriant „Java“ kolekciją, kaip parodyta šiame pavyzdyje.


Pastaba:Vis tiek galite sukurti „Java“ kolekcijos objektą nenurodydami saugomo duomenų tipo, tačiau jis nerekomenduojamas. List stringList = new ArrayList();

Dabar negalite klaidingai išsaugoti sveikojo skaičiaus eilutės tipo sąraše, neišmesdami kompiliavimo laiko klaidos. Tai užtikrina, kad jūsų programoje nebus vykdymo laiko klaidų.

stringList.add(new Integer(4)); //Compile time Error

Pagrindinis generikų įvedimo į „Java“ tikslas buvo išvengti susidūrimo su ClassCastExceptions vykdymo metu.



„Java Generics“ kūrimas

Norėdami sukurti „Java“ klases ir metodus, galite naudoti generinius. Pažvelkime į pavyzdžius, kaip sukurti kiekvieno tipo bendrinius.

Bendroji klasė

Kuriant bendrąją klasę, klasės parametras pridedamas klasės pavadinimo pabaigoje kampu skliausteliuose.


public class GenericClass {
private T item;
public void setItem(T item) {
this.item = item;
}
public T getItem() {
return this.item;
} }

Čia, T yra duomenų tipo parametras. T, N ir E yra keletas raidžių, naudojamų duomenų tipo parametrams pagal „Java“ sutartis.

Ankstesniame pavyzdyje kurdami „GenericClass“ objektą galite perduoti tam tikrą duomenų tipą.

public static void main(String[] args) {
GenericClass gc1 = new GenericClass();
gc1.setItem('hello');
String item1 = gc1.getItem(); // 'hello'
gc1.setItem(new Object()); //Error
GenericClass gc2 = new GenericClass();
gc2.setItem(new Integer(1));
Integer item2 = gc2.getItem(); // 1
gc2.setItem('hello'); //Error }

Negalite perduoti primityvaus duomenų tipo parametrui duomenų tipas, kai kuriate bendrąjį klasės objektą. Tik duomenų tipai, išplėsti objekto tipą, gali būti perduoti kaip tipo parametrai.

Pavyzdžiui:


GenericClass gc3 = new GenericClass(); //Error

Bendrieji metodai

Kuriant bendruosius metodus, panašiai kaip kuriant bendrąsias klases. Bendrąjį metodą galite įdiegti ir bendroje klasėje, ir ne bendroje.

public class GenericMethodClass {
public static void printItems(T[] arr){
for (int i=0; i< arr.length; i++) {

System.out.println(arr[i]);
}
}
public static void main(String[] args) {
String[] arr1 = {'Cat', 'Dog', 'Mouse'};
Integer[] arr2 = {1, 2, 3};

GenericMethodClass.printItems(arr1); // 'Cat', 'Dog', 'Mouse'
GenericMethodClass.printItems(arr2); // 1, 2, 3
} }

Čia galite perduoti tam tikro tipo masyvą, kad būtų galima nustatyti metodą. Bendrasis metodas PrintItems() kartojasi per perduotą masyvą ir spausdina saugomus elementus kaip ir įprastą „Java“ metodą.



Apribotų tipų parametrai

Kol kas mūsų sukurtas bendrąsias klases ir metodus galima nustatyti bet kokiam duomenų tipui, išskyrus primityvius. Bet ką daryti, jei norėtume apriboti duomenų tipus, kuriuos galima perduoti generikams? Čia yra riboto tipo parametrai.

Galite susieti duomenų tipus, priimtus pagal bendrąją klasę ar metodą, nurodydami, kad tai turėtų būti kito tipo duomenų poklasis.


Pavyzdžiui:

//accepts only subclasses of List public class UpperBoundedClass{
//accepts only subclasses of List
public void UpperBoundedMethod(T[] arr) {
} }

Čia, UpperBoundedClass ir UpperBoundedMethod parametrus galima nustatyti tik naudojant List potipius duomenų tipas.

List duomenų tipas veikia kaip viršutinė tipo parametro riba. Jei bandysite naudoti duomenų tipą, kuris nėra List potipis, tai sukels kompiliavimo laiko klaidą.

Ribos neapsiriboja tik klasėmis. Taip pat galite perduoti sąsajas. Sąsajos išplėtimas šiuo atveju reiškia sąsajos diegimą.

Parametras taip pat gali turėti kelias ribas, kaip parodyta šiame pavyzdyje.

//accepts only subclasses of both Mammal and Animal public class MultipleBoundedClass{
//accepts only subclasses of both Mammal and Animal
public void MultipleBoundedMethod(T[] arr){
} }

Priimamas duomenų tipas turi būti gyvūnų ir žinduolių klasių poklasis. Jei viena iš šių ribų yra klasė, privaloma deklaracija turi būti pirmoji.

Ankstesniame pavyzdyje, jei žinduolis yra klasė, o gyvūnas yra sąsaja, žinduolis turi būti pirmas, kaip parodyta aukščiau. Priešingu atveju kodas išmeta kompiliavimo laiko klaidą.



„Java Generics“ pakaitos

Pakaitiniai simboliai naudojami bendrųjų tipų parametrams perduoti metodams. Skirtingai nuo bendro metodo, čia bendras parametras perduodamas metodo priimtiems parametrams, kurie skiriasi nuo aukščiau aptarto duomenų tipo parametro. Pakaitos simbolį vaizduoja? simbolis.

public void printItems(List list) {
for (int i=0; i< list.size(); i++) {
System.out.println(list.get(i));
} }

Aukščiau išvardinta printItems() metodas priima bet kokio tipo duomenų sąrašus kaip parametrą. Tai neleidžia programuotojams kartoti skirtingų duomenų tipų kodų, kaip būtų be generinių.

Viršutinės ribotos pakaitos

Jei norime apriboti duomenų tipus, saugomus metodu priimtame sąraše, galime naudoti apribotas pakaitos simbolius.

Pavyzdys:

public void printSubTypes(List list) {
for (int i=0; i< list.size(); i++) {
System.out.println(list.get(i));
} }

printSubTypes() metodas priima tik sąrašus, kuriuose saugomi spalvų potipiai. Ji priima „RedColor“ arba „BlueColor“ objektų sąrašą, tačiau nepriima gyvūnų objektų sąrašo. Taip yra todėl, kad gyvūnas nėra spalvų potipis. Tai yra viršutinės ribos pakaitos pavyzdys.

Žemiau ribotos pakaitos

Panašiai, jei turėtume:

public void printSuperTypes(List list) {
for (int i=0; i< list.size(); i++) {
System.out.println(list.get(i));
} }

tada printSuperTypes() metodas priima tik sąrašus, kuriuose saugomi super šunų tipai. Ji priimtų žinduolių ar gyvūnų objektų sąrašą, bet ne „LabDog“ objektų sąrašą, nes „LabDog“ yra ne šuns, o poklasis. Tai žemesnės ribos pakaitos pavyzdys.



Išvada

„Java Generics“ tapo savybe, be kurios programuotojai negali gyventi nuo pat jos įvedimo.

Šis populiarumas yra dėl to, kad jis palengvina programuotojų gyvenimą. Išskyrus atvejus, kai neleidžiama daryti kodavimo klaidų, naudojant bendrinius kodus, kodas tampa mažiau kartojamas. Ar pastebėjote, kaip jis apibendrina klases ir metodus, kad nereikėtų kartoti kodo skirtingiems duomenų tipams?

Gerai suvokiant generikus, svarbu tapti kalbos ekspertu. Taigi, tai, ką išmokote šioje pamokoje, pritaikyti praktiniame kode yra būdas eiti toliau.