CodeGym /Blog Java /Aleatoriu /Generic în Java
John Squirrels
Nivel
San Francisco

Generic în Java

Publicat în grup
Bună! Vom vorbi despre Java Generics. Trebuie să spun că vei învăța multe! Nu numai această lecție, ci și următoarele câteva lecții vor fi dedicate genericelor. Deci, dacă sunteți interesat de medicamente generice, astăzi este ziua norocoasă: veți învăța multe despre caracteristicile medicamentelor generice. Și dacă nu, resemnează-te și relaxează-te! :) Acesta este un subiect foarte important și trebuie să-l știți. Să începem cu simplul: „ce” și „de ce”.

Ce sunt Java Generics?

Genericurile sunt tipuri care au un parametru. Când creați un tip generic, specificați nu numai un tip, ci și tipul de date cu care va funcționa. Bănuiesc că cel mai evident exemplu ți-a venit deja în minte: ArrayList! Acesta este modul în care de obicei creăm unul într-un program:

import java.util.ArrayList;
import java.util.List;

public class Main {

   public static void main(String[] args) {

       List<String> myList1 = new ArrayList<>();
       myList1.add("Test String 1");
       myList1.add("Test String 2");
   }
}
După cum ați putea ghici, o caracteristică a acestei liste este că nu putem introduce totul în ea: funcționează exclusiv cu obiecte String . Acum să facem o mică digresiune în istoria Java și să încercăm să răspundem la întrebarea „de ce?” Pentru a face acest lucru, vom scrie propria noastră versiune simplificată a clasei ArrayList. Lista noastră știe doar cum să adăugați date și să preluați date dintr-o matrice internă:

public class MyListClass {

   private Object[] data;
   private int count;

   public MyListClass() {
       this.data = new Object[10];
       this.count = 0;
   }

   public void add(Object o) {
       this.data[count] = o;
       count++;
   }

   public Object[] getData() {
       return data;
   }
}
Să presupunem că vrem ca lista noastră să stocheze numai numere întregi. Nu folosim un tip generic. Nu dorim să includem o verificare explicită „instanceof Integer ” în metoda add() . Dacă am face-o, întreaga noastră clasă ar fi potrivită numai pentru Integer și ar trebui să scriem o clasă similară pentru orice alt tip de date din lume! Ne vom baza pe programatorii noștri și lăsăm doar un comentariu în cod pentru a ne asigura că nu adaugă nimic din ce nu dorim:

// Use this class ONLY with the Integer data type
public void add(Object o) {
   this.data[count] = o;
   count++;
}
Unul dintre programatori a ratat acest comentariu și a pus, din neatenție, mai multe șiruri de caractere într-o listă de numere și apoi a calculat suma lor:

public class Main {

   public static void main(String[] args) {

       MyListClass list = new MyListClass();
       list.add(100);
       list.add(200);
       list.add("Lolkek");
       list.add("Shalala");

       Integer sum1 = (Integer) list.getData()[0] + (Integer) list.getData()[1];
       System.out.println(sum1);

       Integer sum2 = (Integer) list.getData()[2] + (Integer) list.getData()[3];
       System.out.println(sum2);
   }
}
Ieșire din consolă:

300 
Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer 
      at Main.main (Main.java:14)
Care este partea cea mai proastă a acestei situații? Cu siguranță nu neatenția programatorului. Partea cea mai proastă este că codul incorect a ajuns într-un loc important în programul nostru și a fost compilat cu succes. Acum vom întâlni eroarea nu în timpul scrierii codului, ci doar în timpul testării (și acesta este cel mai bun scenariu!). Remedierea erorilor în etapele ulterioare de dezvoltare costă mult mai mult - atât în ​​termeni de bani, cât și de timp. Tocmai aici ne avantajează genericele: o clasă generică îi permite programatorului ghinionist să detecteze eroarea imediat. Programul pur și simplu nu se va compila!

import java.util.ArrayList;
import java.util.List;

public class Main {

   public static void main(String[] args) {

       List<Integer> myList1 = new ArrayList<>();
      
       myList1.add(100);
       myList1.add(100);
       myList1.add ("Lolkek"); // Error!
       myList1.add("Shalala"); // Error!
   }
}
Programatorul își dă imediat seama de greșeala sa și se îmbunătățește instantaneu. Apropo, nu a trebuit să creăm propria noastră clasă List pentru a vedea acest tip de eroare. Pur și simplu eliminați parantezele unghiulare și introduceți ( <Integer> ) dintr-o ArrayList obișnuită!

import java.util.ArrayList;
import java.util.List;

public class Main {

   public static void main(String[] args) {

      List list = new ArrayList();

      list.add(100);
      list.add(200);
      list.add("Lolkek");
      list.add("Shalala");

       System.out.println((Integer) list.get(0) + (Integer) list.get(1));
       System.out.println((Integer) list.get(2) + (Integer) list.get(3));
   }
}
Ieșire din consolă:

300 
Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer 
     at Main.main(Main.java:16)
Cu alte cuvinte, chiar și folosind mecanismele „native” ale Java, putem face acest tip de greșeală și putem crea o colecție nesigură. Cu toate acestea, dacă lipim acest cod într-un IDE, primim un avertisment: „Apel nebifat pentru a adăuga(E) ca membru al tipului brut de java.util.List” Ni se spune că ceva poate merge prost atunci când adăugăm un articol la o colecție care nu are un tip generic. Dar ce înseamnă expresia „tip brut”? Un tip brut este o clasă generică al cărei tip a fost eliminat. Cu alte cuvinte, List myList1 este un tip brut . Opusul unui tip brut este un tip generic — o clasă generică cu o indicație a tipului (tipurilor) parametrizate . De exemplu, List<String> myList1. S-ar putea să vă întrebați de ce limbajul permite utilizarea tipurilor brute ? Motivul este simplu. Creatorii Java au lăsat suportul pentru tipurile brute în limbaj pentru a evita crearea de probleme de compatibilitate. Până la lansarea Java 5.0 (generale au apărut pentru prima dată în această versiune), o mulțime de cod au fost deja scrise folosind tipuri brute . Ca urmare, acest mecanism este susținut și astăzi. Am menționat în repetate rânduri cartea clasică a lui Joshua Bloch „Effective Java” în lecții. Fiind unul dintre creatorii limbii, el nu a omis tipurile brute și tipurile generice în cartea sa. Ce sunt genericele în Java?  - 2Capitolul 23 al cărții are un titlu foarte elocvent: „Nu utilizați tipuri brute în codul nou” Acesta este ceea ce trebuie să vă amintiți. Când utilizați clase generice, nu transformați niciodată un tip generic într-un tip brut .

Metode generice

Java vă permite să parametrizați metode individuale prin crearea așa-numitelor metode generice. Cum sunt utile astfel de metode? Mai presus de toate, sunt utile prin faptul că vă permit să lucrați cu diferite tipuri de parametri de metodă. Dacă aceeași logică poate fi aplicată în siguranță la diferite tipuri, o metodă generică poate fi o soluție excelentă. Luați în considerare acesta un exemplu foarte simplu: Să presupunem că avem o listă numită myList1 . Dorim să eliminăm toate valorile din listă și să umplem toate spațiile goale cu valori noi. Iată cum arată clasa noastră cu o metodă generică:

public class TestClass {

   public static <T> void fill(List<T> list, T val) {
       for (int i = 0; i < list.size(); i++)
           list.set(i, val);
   }

   public static void main(String[] args) {

       List<String> strings = new ArrayList<>();
       strings.add("Old String 1");
       strings.add("Old String 2");
       strings.add("Old String 3");

       fill(strings, "New String");

       System.out.println(strings);

       List<Integer> numbers = new ArrayList<>();
       numbers.add(1);
       numbers.add(2);
       numbers.add(3);

       fill(numbers, 888);
       System.out.println(numbers);
   }
}
Atenție la sintaxă. Pare puțin neobișnuit:

public static <T> void fill(List<T> list, T val)
Scriem <T> înainte de tipul returnat. Acest lucru indică faptul că avem de-a face cu o metodă generică. În acest caz, metoda acceptă 2 parametri ca intrare: o listă de obiecte T și un alt obiect T separat. Folosind <T>, parametrizăm tipurile de parametri ale metodei: nu putem trece o listă de șiruri de caractere și un număr întreg. O listă de șiruri și un șir de caractere, o listă de numere întregi și un număr întreg, o listă a propriilor obiecte Cat și un alt obiect Cat - asta este ceea ce trebuie să facem. Metoda main() ilustrează modul în care metoda fill() poate fi utilizată cu ușurință pentru a lucra cu diferite tipuri de date. Mai întâi, folosim metoda cu o listă de șiruri de caractere și un șir de caractere ca intrare, apoi cu o listă de numere întregi și un număr întreg. Ieșire din consolă:

[New String, New String, New String] [888, 888, 888]
Imaginați-vă dacă nu am avea metode generice și ar avea nevoie de logica metodei fill() pentru 30 de clase diferite. Ar trebui să scriem aceeași metodă de 30 de ori pentru diferite tipuri de date! Dar datorită metodelor generice, putem reutiliza codul nostru! :)

Clasele generice

Nu sunteți limitat la clasele generice furnizate în bibliotecile standard Java - vă puteți crea propriile! Iată un exemplu simplu:

public class Box<T> {

   private T t;

   public void set(T t) {
       this.t = t;
   }

   public T get() {
       return t;
   }

   public static void main(String[] args) {

       Box<String> stringBox = new Box<>();

       stringBox.set("Old String");
       System.out.println(stringBox.get());
       stringBox.set("New String");

       System.out.println(stringBox.get());
      
       stringBox.set(12345); // Compilation error!
   }
}
Clasa noastră Box<T> este o clasă generică. Odată ce atribuim un tip de date ( <T> ) în timpul creării, nu mai putem plasa în el obiecte de alte tipuri. Acest lucru poate fi văzut în exemplu. La crearea obiectului nostru, am indicat că va funcționa cu Strings:

Box<String> stringBox = new Box<>();
Și în ultima linie de cod, când încercăm să punem numărul 12345 în cutie, obținem o eroare de compilare! Este atât de ușor! Am creat propria noastră clasă generică! :) Cu asta, lecția de azi se încheie. Dar nu ne luăm la revedere de la generice! În lecțiile următoare, vom vorbi despre funcții mai avansate, așa că nu plecați! ) Pentru a consolida ceea ce ați învățat, vă sugerăm să urmăriți o lecție video de la Cursul nostru Java
Cel mai bun succes în studiile tale! :)
Comentarii
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION