Bevezetés
A JSE 5.0-tól kezdődően a Java nyelv arzenáljába a generikusok is bekerültek.Mik azok a generikusok a java-ban?
Az általánosok a Java speciális mechanizmusa az általános programozás megvalósítására – az adatok és algoritmusok leírásának módja, amely lehetővé teszi, hogy különböző adattípusokkal dolgozzon anélkül, hogy megváltoztatná az algoritmusok leírását. Az Oracle webhelyen külön oktatóanyag található a generikus szerek számára: " Lecke ". A generikumok megértéséhez először ki kell derítenie, miért van rájuk szükség, és mit adnak. Az oktatóanyag „ Miért használjunk általánosokat? ” szakasza azt mondja, hogy néhány cél az erősebb típusellenőrzés a fordítási időben, és az explicit leadások szükségességének kiküszöbölése.
import java.util.*;
public class HelloWorld {
public static void main(String []args) {
List list = new ArrayList();
list.add("Hello");
String text = list.get(0) + ", world!";
System.out.print(text);
}
}
Ez a kód tökéletesen fog futni. De mi van akkor, ha odajön hozzánk a főnök, és azt mondja, hogy "Hello, world!" túl használt kifejezés, és csak a "Hello"-t kell visszaadnia? Eltávolítjuk a kódot, amely összefűzi a ", world!" Ez elég ártalmatlannak tűnik, igaz? De valójában egy hibát kapunk AZ ÖSSZEÁLLÍTÁSI IDŐBEN:
error: incompatible types: Object cannot be converted to String
A probléma az, hogy a listánkban az objektumokat tároljuk. A String az Object leszármazottja (mivel minden Java osztály implicit módon örökli az Object -et ), ami azt jelenti, hogy szükségünk van egy explicit leadásra, de nem adtunk hozzá. Az összefűzési művelet során a statikus String.valueOf(obj) metódus az objektum segítségével kerül meghívásra. Végül meghívja az Object osztály toString metódusát. Más szavakkal, a listánk egy objektumot tartalmaz . Ez azt jelenti, hogy ahol szükségünk van egy adott típusra (nem az objektumra ), magunknak kell elvégeznünk a típuskonverziót:
import java.util.*;
public class HelloWorld {
public static void main(String []args) {
List list = new ArrayList();
list.add("Hello!");
list.add(123);
for (Object str : list) {
System.out.println("-" + (String)str);
}
}
}
Ebben az esetben azonban, mivel a List objektumokat vesz fel, nem csak String s-t, hanem Integer s-t is tárolhat . De a legrosszabb az, hogy a fordító nem lát itt semmi rosszat. És most hibaüzenetet kapunk AT RUN TIME ("futásidejű hiba"). A hiba a következő lesz:
java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
El kell fogadnod, hogy ez nem túl jó. És mindezt azért, mert a fordító nem egy mesterséges intelligencia, amely képes mindig helyesen kitalálni a programozó szándékát. A Java SE 5 bevezette az általánosokat, hogy elmondhassuk a fordítónak szándékainkat – arról, hogy mely típusokat fogjuk használni. A kódunkat úgy javítjuk, hogy elmondjuk a fordítónak, hogy mit akarunk:
import java.util.*;
public class HelloWorld {
public static void main(String []args) {
List<String> list = new ArrayList<>();
list.add("Hello!");
list.add(123);
for (Object str : list) {
System.out.println("-" + str);
}
}
}
Amint látja, már nincs szükségünk egy karakterláncra leadásra . Ezenkívül szögletes zárójelek veszik körül a típus argumentumot. Most a fordító nem engedi lefordítani az osztályt, amíg el nem távolítjuk azt a sort, amely 123-at ad a listához, mivel ez egy egész szám . És ezt meg is fogja mondani nekünk. Sokan a generikus gyógyszereket „szintaktikai cukornak” nevezik. És igazuk van, mivel a generikumok összeállítása után valóban ugyanolyan típusú konverziókká válnak. Nézzük meg a lefordított osztályok bájtkódját: egy explicit cast-ot használó és egy generikust használó osztály: 
Nyers típusok
Ha már a generikáról beszélünk, mindig két kategóriánk van: paraméterezett típusok és nyers típusok. A nyers típusok olyan típusok, amelyek elhagyják a szögletes zárójelben szereplő "típus-tisztázást":

import java.util.*;
public class HelloWorld {
public static void main(String []args) {
List<String> list = new ArrayList();
list.add("Hello, World");
String data = list.get(0);
System.out.println(data);
}
}
De ez keveri az új stílust a generikusokkal és a régi stílust ezek nélkül. Ez pedig nagyon nem kívánatos. A fenti kód összeállításakor a következő üzenetet kapjuk:
Note: HelloWorld.java uses unchecked or unsafe operations
Valójában érthetetlennek tűnik, hogy miért kell még egy gyémántot hozzáadni. De itt egy példa:
import java.util.*;
public class HelloWorld {
public static void main(String []args) {
List<String> list = Arrays.asList("Hello", "World");
List<Integer> data = new ArrayList(list);
Integer intNumber = data.get(0);
System.out.println(data);
}
}
Emlékszel, hogy az ArrayListnek van egy második konstruktora, amely egy gyűjteményt vesz argumentumként. És ez az, ahol valami baljóslat rejtőzik. A gyémánt szintaxis nélkül a fordító nem érti, hogy megtévesztik. A gyémánt szintaxissal ez igen. Tehát az 1. szabály: mindig használja a gyémánt szintaxist a paraméterezett típusokkal. Ellenkező esetben fennáll annak a veszélye, hogy elveszítjük a nyers típusokat. A "nem bejelölt vagy nem biztonságos műveleteket használ" figyelmeztetések kiküszöbölésére használhatjuk a @SuppressWarnings("unchecked") megjegyzést egy metóduson vagy osztályon. De gondolja át, miért döntött úgy, hogy ezt használja. Emlékezz az első számú szabályra. Lehet, hogy típus argumentumot kell hozzáadnia.
Java általános módszerek
A Generics lehetővé teszi olyan metódusok létrehozását, amelyek paramétertípusai és visszatérési típusa paraméterezett. Az Oracle oktatóanyagának külön szakasza van ennek a képességnek: " Általános módszerek ". Fontos megjegyezni az oktatóanyagban tanított szintaxist:- szögletes zárójelben tartalmazza a típusparaméterek listáját;
- a típusparaméterek listája a metódus visszatérési típusa elé kerül.
import java.util.*;
public class HelloWorld {
public static class Util {
public static <T> T getValue(Object obj, Class<T> clazz) {
return (T) obj;
}
public static <T> T getValue(Object obj) {
return (T) obj;
}
}
public static void main(String []args) {
List list = Arrays.asList("Author", "Book");
for (Object element : list) {
String data = Util.getValue(element, String.class);
System.out.println(data);
System.out.println(Util.<String>getValue(element));
}
}
}
Ha megnézi az Util osztályt, látni fogja, hogy két általános metódusa van. A típuskövetkeztetés lehetőségének köszönhetően vagy közvetlenül jelezhetjük a fordítónak a típust, vagy mi magunk is megadhatjuk. Mindkét lehetőséget bemutatjuk a példában. Egyébként a szintaxisnak sok értelme van, ha belegondolunk. Általános metódus deklarálásakor a type paramétert a metódus ELŐTT adjuk meg, mert ha a type paramétert a metódus után deklaráljuk, akkor a JVM nem tudná kitalálni, hogy melyik típust használja. Ennek megfelelően először deklaráljuk, hogy a T típusú paramétert használjuk , majd azt mondjuk, hogy ezt a típust fogjuk visszaadni. Természetesen az Util.<Integer>getValue(elem, String.class) hiba lép fel:nem kompatibilis típusok: A Class<String> nem konvertálható Class<Integer>-vé . Általános módszerek használatakor mindig emlékezzen a típustörlésre. Nézzünk egy példát:
import java.util.*;
public class HelloWorld {
public static class Util {
public static <T> T getValue(Object obj) {
return (T) obj;
}
}
public static void main(String []args) {
List list = Arrays.asList(2, 3);
for (Object element : list) {
System.out.println(Util.<Integer>getValue(element) + 1);
}
}
}
Ez jól fog működni. De csak addig, amíg a fordító megérti, hogy a meghívott metódus visszatérési típusa Integer . Cserélje ki a konzol kimeneti utasítását a következő sorra:
System.out.println(Util.getValue(element) + 1);
Hibát kapunk:
bad operand types for binary operator '+', first type: Object, second type: int.
Más szóval, típustörlés történt. A fordító azt látja, hogy senki nem adta meg a típust, ezért a típus Object- ként jelenik meg , és a metódus hibával meghiúsul.
Általános osztályok
Nem csak a módszerek paraméterezhetők. Osztályok is. Az Oracle oktatóanyagának „Általános típusok” című szakasza ennek szentelt. Nézzünk egy példát:
public static class SomeType<T> {
public <E> void test(Collection<E> collection) {
for (E element : collection) {
System.out.println(element);
}
}
public void test(List<Integer> collection) {
for (Integer element : collection) {
System.out.println(element);
}
}
}
Itt minden egyszerű. Ha az általános osztályt használjuk, akkor az osztály neve után megjelenik a típus paraméter. Most hozzuk létre ennek az osztálynak a példányát a fő metódusban:
public static void main(String []args) {
SomeType<String> st = new SomeType<>();
List<String> list = Arrays.asList("test");
st.test(list);
}
Ez a kód jól fog futni. A fordító látja, hogy létezik egy számlista és egy karakterlánc - gyűjtemény . De mi van, ha megszüntetjük a type paramétert, és ezt tesszük:
SomeType st = new SomeType();
List<String> list = Arrays.asList("test");
st.test(list);
Hibát kapunk:
java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
Ez megint a típustörlés. Mivel az osztály már nem használ típusparamétert, a fordító úgy dönt, hogy mivel egy Listát adtunk át, a List<Integer> metódus a legmegfelelőbb. És egy hibával elbukunk. Ezért van 2. szabályunk: Ha általános osztályod van, mindig adja meg a típusparamétereket.
Korlátozások
Korlátozhatjuk az általános metódusokban és osztályokban megadott típusokat. Tegyük fel például, hogy azt szeretnénk, hogy egy tároló csak egy számot fogadjon el típus argumentumként. Ezt a funkciót az Oracle oktatóanyagának Korlátozott típusparaméterek szakasza ismerteti . Nézzünk egy példát:
import java.util.*;
public class HelloWorld {
public static class NumberContainer<T extends Number> {
private T number;
public NumberContainer(T number) { this.number = number; }
public void print() {
System.out.println(number);
}
}
public static void main(String []args) {
NumberContainer number1 = new NumberContainer(2L);
NumberContainer number2 = new NumberContainer(1);
NumberContainer number3 = new NumberContainer("f");
}
}
Amint látja, a type paramétert a Number osztályra/interfészre vagy annak leszármazottaira korlátoztuk . Ne feledje, hogy nemcsak osztályt, hanem interfészeket is megadhat. Például:
public static class NumberContainer<T extends Number & Comparable> {
Az általánosok a helyettesítő karaktereket is támogatják. Három típusra oszthatók:
- Felső korlátos helyettesítő karakterek — < ? kiterjeszti Szám >
- Korlátlan helyettesítő karakterek — < ? >
- Alsó korlátos helyettesítő karakterek — < ? szuper egész >
- Használjon kiterjesztett helyettesítő karaktert, ha csak értékeket kap ki egy struktúrából.
- Használjon szuper helyettesítő karaktert, ha csak értékeket ad meg egy szerkezetben.
- És ne használjon helyettesítő karaktert, ha mindketten egy szerkezetet akarsz elérni és elhelyezni.

public static class TestClass {
public static void print(List<? extends String> list) {
list.add("Hello, World!");
System.out.println(list.get(0));
}
}
public static void main(String []args) {
List<String> list = new ArrayList<>();
TestClass.print(list);
}
De ha lecseréled a kiterjesztéseket szuperre , akkor minden rendben van. Mivel a listát a tartalmának megjelenítése előtt feltöltjük egy értékkel, ez fogyasztó . Ennek megfelelően szuper.
Öröklés
A generikának van még egy érdekes tulajdonsága: az öröklődés. Az öröklődés működésének általános leírása az Oracle oktatóanyagának „ Általános adatok, öröklődés és altípusok ” című részében található. A legfontosabb, hogy emlékezzen és ismerje fel a következőket. Ezt nem tudjuk megtenni:
List<CharSequence> list1 = new ArrayList<String>();
Mert az öröklődés másként működik a generikusoknál: 
List<String> list1 = new ArrayList<>();
List<Object> list2 = list1;
Itt megint minden egyszerű. A List<String> nem a List<Object> leszármazottja , bár a String az Object leszármazottja . A tanultak megerősítése érdekében javasoljuk, hogy nézzen meg egy videóleckét a Java-tanfolyamról
Következtetés
Tehát felfrissítettük emlékezetünket a generikus gyógyszerekkel kapcsolatban. Ha ritkán használja ki teljes mértékben a képességeiket, néhány részlet homályossá válik. Remélem, ez a rövid áttekintés segített felfrissíteni a memóriáját. A még jobb eredmények érdekében erősen ajánlom, hogy ismerkedjen meg a következő anyaggal:
További olvasnivalók: |
---|
GO TO FULL VERSION