CodeGym /Blog Jawa /Acak /Tipe erasure
John Squirrels
tingkat
San Francisco

Tipe erasure

Diterbitake ing grup
Hi! Kita nerusake seri pelajaran babagan generik. Kita sadurunge entuk gambaran umum babagan apa lan kenapa dibutuhake. Dina iki kita bakal sinau luwih akeh babagan sawetara fitur generik lan babagan nggarap. Ayo budal! Tipe mbusak - 1Ing wulangan pungkasan , kita ngomong babagan bedane jinis generik lan jinis mentah . Jinis mentah yaiku kelas umum sing jinise wis dibusak.

List list = new ArrayList();
Punika conto. Ing kene kita ora nuduhake jinis obyek apa sing bakal diselehake ing kita List. Yen kita nyoba nggawe Listlan nambah sawetara obyek, kita bakal weruh bebaya ing IDEA:

"Unchecked call to add(E) as a member of raw type of java.util.List".
Nanging kita uga ngomong babagan kasunyatan yen generik mung muncul ing Jawa 5. Nalika versi iki dirilis, programer wis nulis akeh kode nggunakake jinis mentah, mula fitur basa iki ora bisa mandheg, lan kemampuan kanggo nggawe jinis mentah ing Jawa dilestarekake. Nanging, masalah kasebut dadi luwih nyebar. Kaya sing sampeyan ngerteni, kode Java diowahi dadi format kompilasi khusus sing diarani bytecode, sing banjur dieksekusi dening mesin virtual Java. Nanging yen kita sijine informasi bab paramèter jinis ing bytecode sak proses konversi, iku bakal break kabeh kode sadurunge ditulis, amarga ora ana parameter jinis sadurunge Jawa 5! Nalika nggarap generik, ana siji konsep sing penting banget sing kudu sampeyan eling. Iki diarani erasure tipe. Iki tegese kelas ora ngemot informasi babagan parameter jinis. Informasi iki mung kasedhiya sajrone kompilasi lan dibusak (dadi ora bisa diakses) sadurunge runtime. Yen sampeyan nyoba nglebokake jinis obyek sing salah ing List<String>, compiler bakal nggawe kesalahan. Iki persis sing dikarepake para pangripta basa nalika nggawe generik: mriksa wektu kompilasi. Nanging nalika kabeh kode Java sampeyan dadi bytecode, ora ana informasi babagan paramèter jinis. Ing bytecode, List<Cat>dhaptar kucing sampeyan ora beda karo List<String>strings. Ing bytecode, ora ana sing ujar manawa catsminangka dhaptar Catobyek. Informasi kasebut dibusak sajrone kompilasi - mung kasunyatan manawa sampeyan duwe dhaptar List<Object> catssing bakal ana ing bytecode program. Ayo ndeleng cara kerjane:

public class TestClass<T> {

   private T value1;
   private T value2;

   public void printValues() {
       System.out.println(value1);
       System.out.println(value2);
   }

   public static <T> TestClass<T> createAndAdd2Values(Object o1, Object o2) {
       TestClass<T> result = new TestClass<>();
       result.value1 = (T) o1;
       result.value2 = (T) o2;
       return result;
   }

   public static void main(String[] args) {
       Double d = 22.111;
       String s = "Test String";
       TestClass<Integer> test = createAndAdd2Values(d, s);
       test.printValues();
   }
}
Kita nggawe kelas umum kita dhewe TestClass. Iku cukup prasaja: iku bener "koleksi" cilik 2 obyek, kang disimpen langsung nalika obyek digawe. Wis 2 Tlapangan. Nalika createAndAdd2Values()cara dieksekusi, loro obyek liwati ( Object alan Object bkudu dibuwang menyang Tjinis banjur ditambahake menyang TestClassobyek. Ing main()cara, kita nggawe TestClass<Integer>, IE Integerargumen jinis nggantèkaké Integerparameter jinis. Kita uga maringaken a Doublelan a Stringkanggo metode createAndAdd2Values(). Apa sampeyan mikir program kita bakal bisa digunakake? Sawise kabeh, kita nemtokake Integerminangka argumen tipe, nanging Stringmesthi ora bisa dilebokake menyang Integer! Ayo mbukakmain()metode lan mriksa. Output konsol:

22.111 
Test String
Sing ora dikarepke! Yagene iki kedadeyan? Iku asil saka jinis erasure. Informasi babagan Integerargumen jinis sing digunakake kanggo instantiate TestClass<Integer> testobyek kita wis dibusak nalika kode dikompilasi. Lapangan dadi TestClass<Object> test. Kita Doublelan Stringbantahan padha gampang diowahi kanggo Objectobyek (padha ora diowahi kanggo Integerobyek kita samesthine!) Lan quietly ditambahake menyang TestClass. Mangkene conto liyane sing gampang nanging jelas babagan mbusak jinis:

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

public class Main {

   private class Cat {

   }

   public static void main(String[] args) {

       List<String> strings = new ArrayList<>();
       List<Integer> numbers = new ArrayList<>();
       List<Cat> cats = new ArrayList<>();

       System.out.println(strings.getClass() == numbers.getClass());
       System.out.println(numbers.getClass() == cats.getClass());

   }
}
Output konsol:

true 
true
Kayane kita nggawe koleksi kanthi telung jinis argumen - String, Integer, lan kelas kita dhewe Cat. Nanging sajrone konversi menyang bytecode, kabeh dhaptar telu dadi List<Object>, dadi nalika program kasebut mlaku ngandhani yen kita nggunakake kelas sing padha ing kabeh telung kasus.

Ketik erasure nalika nggarap array lan generik

Ana titik penting sing kudu dingerteni kanthi jelas nalika nggarap array lan kelas umum (kayata List). Sampeyan uga kudu dipikirake nalika milih struktur data kanggo program sampeyan. Generik tundhuk jinis erasure. Informasi babagan paramèter jinis ora kasedhiya nalika runtime. Ing kontras, array ngerti lan bisa nggunakake informasi babagan jinis data nalika program lagi mlaku. Nyoba nglebokake jinis sing ora bener menyang array bakal nyebabake pangecualian dibuwang:

public class Main2 {

   public static void main(String[] args) {

       Object x[] = new String[3];
       x[0] = new Integer(222);
   }
}
Output konsol:

Exception in thread "main" java.lang.ArrayStoreException: java.lang.Integer
Amarga ana bedane gedhe antarane array lan generik, bisa uga ana masalah kompatibilitas. Sing paling penting, sampeyan ora bisa nggawe array obyek umum utawa mung array parameter. Apa sing muni rada bingung? Ayo dideleng. Contone, sampeyan ora bisa nindakake iki ing Jawa:

new List<T>[]
new List<String>[]
new T[]
Yen kita nyoba nggawe macem-macem List<String>obyek, kita entuk kesalahan kompilasi sing ngeluh babagan nggawe array umum:

import java.util.List;

public class Main2 {

   public static void main(String[] args) {

       // Compilation error! Generic array creation
       List<String>[] stringLists = new List<String>[1];
   }
}
Nanging kenapa iki rampung? Napa nggawe array kasebut ora diidini? Iki kabeh kanggo nyedhiyani safety jinis. Yen kompiler ngidini kita nggawe susunan obyek umum, kita bisa nggawe akeh masalah kanggo awake dhewe. Punika conto prasaja saka buku Joshua Bloch "Jawa Efektif":

public static void main(String[] args) {

   List<String>[] stringLists = new List<String>[1];  //  (1)
   List<Integer> intList = Arrays.asList(42, 65, 44);  //  (2)
   Object[] objects = stringLists;  //  (3)
   objects[0] = intList;  //  (4)
   String s = stringLists[0].get(0);  //  (5)
}
Ayo mbayangno manawa nggawe array kaya List<String>[] stringListsdiidini lan ora bakal ngasilake kesalahan kompilasi. Yen iki bener, ana sawetara perkara sing bisa ditindakake: Ing baris 1, kita nggawe dhaptar dhaptar List<String>[] stringLists:. Array kita ngemot siji List<String>. Ing baris 2, kita nggawe dhaptar nomer: List<Integer>. Ing baris 3, kita nemtokake kita List<String>[]menyang Object[] objectsvariabel. Basa Jawa ngidini iki: macem-macem Xobyek bisa nyimpen Xobyek lan obyek kabeh subkelas X. Patut, sampeyan bisa nyelehake apa wae ing Objectarray. Ing baris 4, kita ngganti siji-sijine unsur array objects()(a List<String>) karo a List<Integer>. Mangkono, kita sijine List<Integer>ing Uploaded sing mung dimaksudaké kanggo nyimpenList<String>obyek! Kita bakal nemokke kesalahan mung nalika kita nglakokaké baris 5. A ClassCastExceptionbakal di buwang ing runtime. Mulane, larangan kanggo nggawe array kasebut ditambahake ing Jawa. Iki ngidini kita nyingkiri kahanan kaya ngono.

Kepiye carane bisa ngubengi jinis erasure?

Inggih, kita sinau babagan jinis erasure. Ayo nyoba ngapusi sistem! :) Tugas: Kita duwe kelas umum TestClass<T>. Kita pengin nulis createNewT()cara kanggo kelas iki sing bakal nggawe lan ngasilake Tobyek anyar. Nanging iki mokal, ta? Kabeh informasi babagan Tjinis kasebut dibusak sajrone kompilasi, lan nalika runtime kita ora bisa nemtokake jinis obyek sing kudu digawe. Ana siji cara angel kanggo nindakake iki. Mbokmenawa sampeyan kelingan yen Jawa duwe Classkelas. Kita bisa nggunakake kanggo nemtokake kelas samubarang obyek kita:

public class Main2 {

   public static void main(String[] args) {

       Class classInt = Integer.class;
       Class classString = String.class;

       System.out.println(classInt);
       System.out.println(classString);
   }
}
Output konsol:

class java.lang.Integer 
class java.lang.String
Nanging ing kene ana siji aspek sing durung dirembug. Ing dokumentasi Oracle, sampeyan bakal weruh yen kelas Kelas iku umum! Tipe mbusak - 3

https://docs.oracle.com/javase/8/docs/api/java/lang/Class.html

Dokumentasi kasebut ujar, "T - jinis kelas sing dimodelake dening obyek Kelas iki." Nerjemahake iki saka basa dokumentasi kanggo wicara biasa, kita ngerti sing kelas obyek Integer.classora mung Class, nanging rodo Class<Integer>. Jinis obyek kasebut String.classora mung Class, nanging Class<String>, lsp. Yen isih durung jelas, coba tambahake parameter jinis ing conto sadurunge:

public class Main2 {

   public static void main(String[] args) {

       Class<Integer> classInt = Integer.class;
       // Compilation error!
       Class<String> classInt2 = Integer.class;
      
      
       Class<String> classString = String.class;
       // Compilation error!
       Class<Double> classString2 = String.class;
   }
}
Lan saiki, nggunakake kawruh iki, kita bisa ngliwati jinis erasure lan ngrampungake tugas kita! Ayo coba golek informasi babagan parameter jinis. Argumen jinis kita bakal dadi MySecretClass:

public class MySecretClass {

   public MySecretClass() {

       System.out.println("A MySecretClass object was created successfully!");
   }
}
Lan iki carane nggunakake solusi ing praktik:

public class TestClass<T> {

   Class<T> typeParameterClass;

   public TestClass(Class<T> typeParameterClass) {
       this.typeParameterClass = typeParameterClass;
   }

   public T createNewT() throws IllegalAccessException, InstantiationException {
       T t = typeParameterClass.newInstance();
       return t;
   }

   public static void main(String[] args) throws InstantiationException, IllegalAccessException {

       TestClass<MySecretClass> testString = new TestClass<>(MySecretClass.class);
       MySecretClass secret = testString.createNewT();

   }
}
Output konsol:

A MySecretClass object was created successfully!
Kita mung ngliwati argumen kelas sing dibutuhake menyang konstruktor kelas umum kita:

TestClass<MySecretClass> testString = new TestClass<>(MySecretClass.class);
Iki ngidini kita nyimpen informasi babagan argumen jinis, nyegah supaya ora dibusak kabeh. Akibaté, kita padha bisa nggawe aTobyek! :) Kanthi mekaten, wulangan dinten punika sampun rampung. Sampeyan kudu tansah ngelingi jinis erasure nalika nggarap generik. Solusi iki katon ora trep banget, nanging sampeyan kudu ngerti manawa generik dudu bagean saka basa Jawa nalika digawe. Fitur iki, sing mbantu kita nggawe koleksi paramèter lan nyekel kesalahan sajrone kompilasi, banjur ditangkep mengko. Ing sawetara basa liyane sing kalebu generik saka versi pisanan, ora ana jinis erasure (contone, ing C #). Miturut cara, kita durung rampung sinau generik! Ing wulangan sabanjure, sampeyan bakal ngerti sawetara fitur generik liyane. Saiki, luwih becik ngrampungake sawetara tugas! :)
Komentar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION