Zašto Java ne može implementirati prave generičke tipove?
„Erge, zašto Java ne može implementirati prave generičke tipove?“ Sanmej je postavila pitanje vrlo direktno.
Jednostavno da se podsetimo brisanja tipova, pogledaj sledeći kod.
public class Cmower {
public static void method(ArrayList<String> list) {
System.out.println("Arraylist<String> list");
}
public static void method(ArrayList<Date> list) {
System.out.println("Arraylist<Date> list");
}
}„Sanmej, misliš li da su ArrayList<String> list i ArrayList<Date> list dva različita tipa, zato što su String i Date različite klase.“ Samo sam pitao.
„Mhm.“ Sanmej je prijala.
Ali zbog brisanja tipova, gornji kod neće proći kompajliranje — kompajler će prijaviti grešku:
>'method(ArrayList<String>)' se sukobljava sa 'method(ArrayList<Date>)'; obe metode imaju isto brisanjeTo znači, dve method() metode nakon brisanja tipova imaju potpuno isti potpis, Java ne dozvoljava ovo.
To znači, prema našoj pretpostavci: ako Java može implementirati prave generičke tipove u pravom smislu, dve method() metode mogu postojati istovremeno, kao overload metode.
public class Cmower {
public static void method(String list) {
}
public static void method(Date list) {
}
}„Zašto Java ne može implementirati prave generičke tipove u pravom smislu? Koja je razlog iza toga? Brzo mi reci!“ Sanmej je hitna.
„Imaj strpljenja, valja?“ Utesio sam.
Prvi, kompatibilnost
Do 2004. godine Java je već imala bogatiji ekosistem, ako postojeće klase menjamo u generičke klase, potrebno je da svi korisnici ponovo modificiraju izvorni kod i kompajliraju, to će dovesti do toga da se carstvo koje je Java stekla pre 1.4 verzije može potpuno srušiti.
Zamislite, vaš kod je prvobitno radio dobro, samo zbog nadogradnje JDK-a, ceo izvorni kod ne može proći kompajliranje i ne može se izvršavati, da li će biti vrlo bolno?
Brisanje tipova savršeno realizuje kompatibilnost, klase posle Java 1.5 mogu koristiti generičke tipove, dok klase pre 1.5 bez generičkih tipova takođe mogu ostati, i bez ikakvih modifikacija mogu raditi na novijoj verziji Java virtuelne mašine.
Stari korisnici su pogođeni, novi korisnici mogu slobodno birati da li da koriste generičke tipove, ubistvo dve muve jednim udarcem.
Drugi, nije „da se ne može realizovati“. Pizza, eksperimentalni jezik iz 1996. godine, proširio je generičke tipove na osnovu Jave.
Adresa tutorijala Pizza: http://pizzacompiler.sourceforge.net/doc/tutorial.html
„1996. godina?“ Sanmej je bila vrlo iznenađena.
„Mhm, te još nisi bila rođena.“ Rekao sam, „dodajmo malo istoriju Java verzija, imaćeš pojam vremenske linije."
- maja 1995., rođen je Java jezik
- Januar 1996., rođen je JDK1.0
- februara 1997., objavljen je JDK1.1
- Februara 1998., JDK1.1 je preuzet više od 2.000.000 puta
- maja 2000., objavljen je JDK1.3
- maja 2000., objavljen je JDK1.4
- septembra 2004. u 18:00, objavljen je J2SE1.5
To znači, Pizza je na JDK 1.0 verziji realizovala „prave generičke tipove“, doneo sam dva primera, odmah ćeš razumeti.
Prvo StoreSomething, generička klasa, identifikator je veliko slovo A, a ne veliko slovo T na koje smo navikli.
class StoreSomething<A> {
A something;
StoreSomething(A something) {
this.something = something;
}
void set(A something) {
this.something = something;
}
A get() {
return something;
}
}Ovo A može biti bilo koji legalni Java tip:
StoreSomething<String> a = new StoreSomething("I'm a string!");
StoreSomething<int> b = new StoreSomething(17+4);
b.set(9);
int i = b.get();
String s = a.get();Zaista, ovo je „pravi generički tip“ koji želimo, A ne može biti samo referentni tip String, već i osnovni tip podataka. Treba znati, Java generički tipovi ne dozvoljavaju osnovne tipove podataka, samo tipove omotača.

Pored toga, Pizza generički tipovi mogu direktno koristiti new ključnu reč za deklaraciju, i Pizza kompajler može zaključiti konkretan tip objekta iz parametara konstruktora, bilo da je String ili int. Treba znati, zbog brisanja tipova Java generičkih tipova, programer ne može znati da li je ArrayList ArrayList<String> ili ArrayList<Integer>.
ArrayList<Integer> ints = new ArrayList<Integer>();
ArrayList<String> strs = new ArrayList<String>();
System.out.println(ints.getClass());
System.out.println(strs.getClass());Rezultat:
class java.util.ArrayList
class java.util.ArrayListSamo ArrayList.
„Zašto Java nije usvojila Pizza takve „prave generičke tipove“?“ Sanmej je vrlo brinula ovo pitanje.
Zapravo, Java core development grupa je vrlo zainteresovana za Pizza generički dizajn, i kontaktirala je sa dizajnerima Pizza Martina i Phila, saradjujući na novom projektu Generic Java, nastojeći da dodaju podršku za generičke tipove u Javi, ali ne uvodeći ostale funkcije Pizza, kao funkcionalno programiranje.
Ovde dodatno malo informacija iz Vikipedije, Martin Odersky je nemački računarski naučnik, on i drugi su dizajnirali Scala programski jezik, i Generic Java (i prethodnu Pizza), on je implementirao Generic Java kompajler koji je postao osnova Java kompajlera javac.
Iz perspektive istorije, Pizza generički dizajn i funkcionalno programiranje su vrlo istorijski perspektivni. Međutim, Java core development grupa se u to vreme činilo da ne želi da uvede funkcionalno programiranje u Javi.
Tako da Java pre 1.5 i dalje ne podržava generičke tipove.
„Zašto Java 1.5 iznenada podržava generičke tipove?“ pita Sanmej.
„Naravno, zato što je vreme došlo do kritične tačke.“ Rekao sam.
Pre generičkih tipova, možemo napisati kod ovako:
ArrayList list = new ArrayList();
list.add("Chenmo Wang Er");
list.add(new Date());Bilo da je String tip, ili Date tip, sve se može metnuti u ArrayList, ovo izgleda vrlo pogodno, ali se kasnije desi tragedija.
String s = list.get(1);Da li ovo može uzeti? Ne. Treba dodati prisilnu konverziju.
String s = (String) list.get(1);Ali znamo, ova linija koda će se sigurno desiti greška pri izvršavanju:
Exception in thread "main" java.lang.ClassCastException: java.util.Date cannot be cast to java.lang.StringVratili smo se na problem „kompatibilnosti“.
Java jezik se razlikuje od drugih programskih jezika, ima tešku istorijsku snagu, pre 1.5 već je bilo veliki broj programa uvedenih u proizvodnju, u ovom trenutku ako se jednom prekine, izvorni kod koji nije koristio generičke tipove direktno ubije, posledice su neizmisljive.
Java uvek naglašava kompatibilnost, mislim da je ovo jedan od glavnih razloga zašto Java može biti široko korišćena, programeri ne moraju brinuti o problemu nadogradnje Java verzije, kod koji može raditi na JDK 1.4, i dalje može raditi na JDK 1.5.
Ovde mora biti naglašeno jednu tačku, izdavanje J2SE1.5 je važna prekretnica u istoriji razvoja Java jezika, kako bi se naglasila važnost ove verzije, J2SE1.5 je formalno preimenovana u Java SE 5.0, kasnije će biti Java SE 6.0, Java SE 7.0...
Ali Java ne podržava da bajt kodovi generisani višom verzijom JDK-a rade na nižoj JRE (Java runtime environment).

Za generičke tipove, gde se konkretno manifestuje kompatibilnost? Pogledaj sledeći kod.
ArrayList<Integer> ints = new ArrayList<Integer>();
ArrayList<String> strs = new ArrayList<String>();
ArrayList list;
list = ints;
list = strs;„Kompatibilnost se manifestuje u tome što gornji kod mora moći da se kompajlira i izvrši. Šta raditi?“ Okrenuo sam se i pogledao Sanmej pored sebe, nastavio sam da govorim.
„Može samo uraditi brisanje tipova!“ Moj sestra je sigurno odgovorila.
„Tačno."
Pre kompajliranja se vrši provera generičkih tipova, ArrayList<Integer> može samo smestiti Integer, ArrayList<String> može samo smestiti String, pri uzimanju nije potrebno brinuti o grešci prisilne konverzije tipova.
Ali u kompajliranom bajt kodu nema generičkih tipova, sve se stavlja Object.
Java je čudna, na površini sve je objekat, ali zbog perfomanskih razmatranja, postoje i originalni tipovi kao int, double, ali originalni tipovi ne mogu biti kompatibilni sa Object, stoga možemo samo pisati ArrayList<Integer> takav kod koji zauzima mnogo memorijskog prostora.
Ovo je verovatno jedan od razloga zašto su Java generički tipovi kritikovani.
Dobra vest je da Project Valhalla nastoji da reši ove istorijske probleme izazvane brisanjem generičkih tipova.

Project Valhalla: OpenJDK projekat u toku, planira da doda poboljšanu podršku za generičke tipove u budućoj Javi.
Adresa izvornog koda: http://openjdk.java.net/projects/valhalla/
Sačekajmo i videćemo!
„Kako ti se dopada? Sanmej, sada sve razumemš?“ Pitao sam.
„Mhm. Erge, ti stvarno dobro objašnjavaš👍“ Sanmej me pohvalila malo srećno, hehe.
