CodeGym /Java blog /Véletlen /Generics Java nyelven
John Squirrels
Szint
San Francisco

Generics Java nyelven

Megjelent a csoportban
Szia! A Java Genericsről fogunk beszélni. Azt kell mondanom, hogy sokat fogsz tanulni! Nemcsak ez a lecke, hanem a következő néhány óra is a generikus terápiának lesz szentelve. Ha tehát érdeklődik a generikumok iránt, ma szerencsés napja van: sok mindent megtudhat a generikumok jellemzőiről. És ha nem, akkor mondjon le és lazítson! :) Ez egy nagyon fontos téma, és ezt tudni kell. Kezdjük az egyszerűvel: a „mit” és a „miért”.

Mik azok a Java Generics?

Az általánosok olyan típusok, amelyeknek van paraméterük. Az általános típus létrehozásakor nem csak egy típust kell megadnia, hanem azt az adattípust is, amellyel dolgozni fog. Gondolom, a legszembetűnőbb példa már eszedbe jutott: ArrayList! Általában így készítünk egyet egy programban:

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");
   }
}
Ahogy sejthető, ennek a listának az a sajátossága, hogy nem tudunk mindent belerakni: kizárólag String objektumokkal működik. Most vessünk egy kis kitérőt a Java történetébe, és próbáljunk meg válaszolni a „miért?” kérdésre. Ehhez megírjuk az ArrayList osztály saját egyszerűsített változatát. A listánk csak azt tudja, hogyan kell adatokat hozzáadni egy belső tömbhöz és lekérni az adatokat:

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;
   }
}
Tegyük fel, hogy azt szeretnénk, hogy a listánk csak egész számokat tároljon. Nem általános típust használunk. Nem akarunk explicit "instanceof Integer " ellenőrzést beépíteni az add() metódusba. Ha megtennénk, akkor az egész osztályunk csak az Integer -re lenne alkalmas , és a világ összes többi adattípusához hasonló osztályt kellene írnunk! Bízunk a programozóinkban, és csak megjegyzést kell hagynunk a kódban, hogy megbizonyosodjunk arról, hogy nem adnak hozzá semmit, amit nem szeretnénk:

// Use this class ONLY with the Integer data type
public void add(Object o) {
   this.data[count] = o;
   count++;
}
Az egyik programozó figyelmen kívül hagyta ezt a megjegyzést, és véletlenül több karakterláncot is elhelyezett egy számlistában, majd kiszámolta az összegüket:

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);
   }
}
Konzol kimenet:

300 
Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer 
      at Main.main (Main.java:14)
Mi a legrosszabb része ennek a helyzetnek? Természetesen nem a programozó figyelmetlensége miatt. A legrosszabb az, hogy a helytelen kód a programunk fontos helyére került, és sikeresen lefordítottuk. Most nem kódírás közben fogunk találkozni a hibával, hanem csak tesztelés közben (és ez a legjobb eset!). A hibák javítása a fejlesztés későbbi szakaszaiban sokkal többe kerül – mind pénzben, mind időben. Pontosan ez az, ahol a generikusok hasznunkra válnak: egy általános osztály segítségével a szerencsétlen programozó azonnal észleli a hibát. A program egyszerűen nem áll le!

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!
   }
}
A programozó azonnal rájön a hibájára, és azonnal jobban lesz. Egyébként nem kellett saját List osztályt létrehoznunk, hogy láthassuk ezt a fajta hibát. Egyszerűen távolítsa el a szögletes zárójeleket, és írja be ( <Integer> ) egy közönséges ArrayList-ből!

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));
   }
}
Konzol kimenet:

300 
Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer 
     at Main.main(Main.java:16)
Más szavakkal, még a Java „natív” mechanizmusait használva is elkövethetünk ilyen hibát, és nem biztonságos gyűjteményt hozhatunk létre. Ha azonban beillesztjük ezt a kódot egy IDE-be, figyelmeztetést kapunk: "Nem ellenőrzött hívás az add(E)-hez a java.util.List nyers típusának tagjaként" Azt mondják, hogy valami elromolhat egy elem hozzáadásakor. olyan gyűjteményhez, amelyből hiányzik az általános típus. De mit jelent a „nyers típus” kifejezés? A nyers típus egy általános osztály, amelynek típusát eltávolították. Más szavakkal, a List myList1 egy nyers típus . A nyers típus ellentéte egy általános típus – egy általános osztály a paraméterezett típus(ok) jelzésével . Például List<String> myList1. Felmerülhet a kérdés, hogy a nyelv miért engedi meg a nyers típusok használatát ? Az ok egyszerű. A Java készítői meghagyták a nyers típusok támogatását a nyelvben, hogy elkerüljék a kompatibilitási problémákat. Mire a Java 5.0 megjelent (ebben a verzióban jelentek meg először az általánosságok), sok kódot már írtak nyers típusokkal . Ennek eredményeként ez a mechanizmus ma is támogatott. A leckéken többször is megemlítettük Joshua Bloch klasszikus könyvét, az „Effective Java” (Hatékony Java). A nyelv egyik megalkotójaként könyvében nem hagyta ki a nyers és általános típusokat .Mik azok a generikusok a Java-ban?  - 2A könyv 23. fejezetének nagyon beszédes a címe: "Ne használj nyers típusokat az új kódban" Erre kell emlékezned. Általános osztályok használatakor soha ne alakítson általános típust nyers típussá .

Általános módszerek

A Java lehetővé teszi az egyes metódusok paraméterezését úgynevezett általános metódusok létrehozásával. Mennyire hasznosak az ilyen módszerek? Mindenekelőtt abban hasznosak, hogy különböző típusú metódusparaméterekkel dolgozhatunk. Ha ugyanaz a logika biztonságosan alkalmazható különböző típusokra, akkor egy általános módszer nagyszerű megoldás lehet. Tekintsük ezt egy nagyon egyszerű példának: Tegyük fel, hogy van egy myList1 nevű listánk . Szeretnénk eltávolítani az összes értéket a listáról, és minden üres helyet kitölteni új értékekkel. Így néz ki az osztályunk egy általános módszerrel:

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);
   }
}
Ügyeljen a szintaxisra. Kicsit szokatlannak tűnik:

public static <T> void fill(List<T> list, T val)
<T>-t írunk a visszatérési típus elé. Ez azt jelzi, hogy általános módszerrel van dolgunk. Ebben az esetben a metódus 2 paramétert fogad be bemenetként: a T objektumok listáját és egy másik különálló T objektumot. A <T> használatával paraméterezzük a metódus paramétertípusait: nem adhatunk át egy karakterláncot és egy egész számot. A karakterláncok és egy karakterlánc listája, egész számok és egész számok listája, saját Cat objektumaink listája és egy másik Cat objektum – ezt kell tennünk. A main() metódus azt szemlélteti, hogy a fill() metódus hogyan használható könnyen különböző típusú adatokkal való munkavégzésre. Először a metódust használjuk a karakterláncok és egy karakterlánc bemeneti listájával, majd az egészek és egy egész számok listájával. Konzol kimenet:

[New String, New String, New String] [888, 888, 888]
Képzeljük el, ha nem léteznének általános metódusaink, és szükségünk lenne a fill() metódus logikájára 30 különböző osztályhoz. Ugyanazt a metódust 30-szor kellene leírnunk különböző adattípusokhoz! De az általános módszereknek köszönhetően újra felhasználhatjuk kódunkat! :)

Általános osztályok

Ön nem korlátozódik a szabványos Java-könyvtárak általános osztályaira – létrehozhatja saját osztályait! Íme egy egyszerű példa:

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!
   }
}
A Box<T> osztályunk egy általános osztály. Ha a létrehozás során hozzárendelünk egy adattípust ( <T> ), abban már nem tudunk más típusú objektumokat elhelyezni. Ez látható a példában. Objektumunk létrehozásakor jeleztük, hogy a Strings-szel működne:

Box<String> stringBox = new Box<>();
És az utolsó kódsorban, amikor megpróbáljuk a 12345-ös számot beírni a dobozba, fordítási hibát kapunk! Ilyen egyszerű! Létrehoztuk saját általános osztályunkat! :) Ezzel a mai lecke véget is ér. De nem mondunk búcsút a generikus gyógyszereknek! A következő leckékben a fejlettebb funkciókról fogunk beszélni, szóval ne menj el! ) A tanultak megerősítése érdekében javasoljuk, hogy nézzen meg egy videóleckét a Java-tanfolyamról
Sok sikert a tanuláshoz! :)
Hozzászólások
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION