JavaRush /Blog Jawa /Random-JV /Ngilangi jinis

Ngilangi jinis

Diterbitake ing grup
Hello! Kita nerusake seri ceramah babagan generik. Sadurungé , kita ngerti ing istilah umum apa iku lan ngapa perlu. Dina iki kita bakal ngomong babagan sawetara fitur generik lan ndeleng sawetara pitfalls nalika nggarap. Tindak! Busak jinis - 1Ing kuliah pungkasan, kita ngomong babagan bedane antarane Jinis Umum lan Jinis Mentah . Yen sampeyan kelalen, Raw Type minangka kelas umum sing jinise wis dibusak.
List list = new ArrayList();
Punika conto. Ing kene kita ora nemtokake jinis obyek sing bakal dilebokake ing List. Yen kita nyoba nggawe siji 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 versi basa Jawa 5. Nalika dirilis, programer wis nulis akeh kode nggunakake Raw Types, lan supaya ora mandheg, kemampuan kanggo nggawe lan nggarap Raw Types in Java dilestarekake. Nanging, masalah iki dadi luwih jembar. Kode Java, kaya sing sampeyan ngerteni, diowahi dadi bytecode khusus, sing banjur dieksekusi dening mesin virtual Java. Lan yen sajrone proses terjemahan kita nyelehake informasi babagan jinis parameter menyang bytecode, bakal ngilangi kabeh kode sing wis ditulis sadurunge, amarga sadurunge Java 5 ora ana jinis parameter! Nalika nggarap generik, ana siji fitur sing penting banget sing kudu sampeyan eling. Iki diarani jinis erasure. Intine dumunung ing kasunyatan manawa ora ana informasi babagan jinis parameter sing disimpen ing kelas kasebut. Informasi iki mung kasedhiya ing tataran kompilasi lan dibusak (dadi ora bisa diakses) nalika runtime. Yen sampeyan nyoba nglebokake obyek saka jinis sing salah menyang List<String>, compiler bakal nggawe kesalahan. Iki persis apa sing digayuh pangripta basa kanthi nggawe generik - mriksa ing tahap kompilasi. Nanging nalika kabeh kode Jawa sing sampeyan tulis dadi bytecode, ora bakal ana informasi babagan jinis parameter. Ing bytecode, dhaptar List<Cat>kucing sampeyan ora bakal beda karo List<String>senar. Ora ana ing bytecode sing bakal ujar manawa catsiki minangka dhaptar obyek Cat. Informasi babagan iki bakal dibusak sajrone kompilasi, lan mung informasi sing sampeyan duwe dhaptar tartamtu ing program sampeyan bakal mlebu kode byte List<Object> cats. 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: ateges iku "koleksi" cilik saka 2 obyek, kang diselehake ana langsung nalika obyek digawe. Wis 2 obyek minangka lapangan T. Nalika metode kasebut dieksekusi, createAndAdd2Values()rong obyek sing dilewati kudu dibuwang Object amenyang Object bjinis kita T, banjur ditambahake menyang obyek kasebut TestClass. Ing cara main()kita nggawe TestClass<Integer>, sing, ing kualitas Tkita bakal duwe Integer. Nanging ing wektu sing padha, createAndAdd2Values()kita ngirim nomer Doublelan obyek menyang metode kasebut String. Apa sampeyan mikir program kita bakal bisa digunakake? Sawise kabeh, kita nemtokake minangka jinis parameter Integer, nanging Stringmesthi ora bisa dibuwang Integer! Ayo mbukak cara main()lan mriksa. Output konsol: 22.111 Test String Asil sing ora dikarepke! Yagene iki kedadeyan? Justru amarga jinis erasure. Sajrone kompilasi kode, informasi babagan jinis parameter Integerobyek kita TestClass<Integer> testwis dibusak. Dheweke dadi TestClass<Object> test. Paramèter kita diowahi dadi tanpa masalah Double( lan ora dadi , kaya sing dikarepake!) lan ditambahake kanthi tenang . Mangkene conto liyane sing gampang nanging nggambarake jinis penghapusan: StringObjectIntegerTestClass
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: bener bener Iku misale jek sing kita wis nggawe koleksi karo telung jinis parameter beda - String, Integer, lan kelas kita digawe Cat. Nanging sajrone konversi menyang bytecode, kabeh telung dhaptar dadi List<Object>, dadi nalika dieksekusi, program kasebut ngandhani yen ing kabeh telung kasus kita nggunakake kelas sing padha.

Ketik erasure nalika nggarap array lan generik

Ana siji titik penting banget sing kudu dimangerteni kanthi cetha nalika nggarap array lan generik (contone, List). Sampeyan uga kudu dipikirake nalika milih struktur data kanggo program sampeyan. Generik tundhuk jinis erasure. Informasi babagan jinis parameter ora kasedhiya sajrone eksekusi program. Ing kontras, array ngerti lan bisa nggunakake informasi babagan jinis data sajrone eksekusi program. Nyoba nglebokake nilai jinis sing salah menyang array bakal mbuwang pengecualian:
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 prabédan gedhe antarane array lan generik, padha bisa duwe masalah kompatibilitas. Kaping pisanan, sampeyan ora bisa nggawe array obyek umum utawa malah mung array diketik. Muni rada bingung? Ayo dideleng kanthi cetha. Contone, sampeyan ora bisa nindakake iki ing Jawa:
new List<T>[]
new List<String>[]
new T[]
Yen kita nyoba nggawe macem-macem dhaptar List<String>, kita entuk kesalahan kompilasi nggawe array umum:
import java.util.List;

public class Main2 {

   public static void main(String[] args) {

       //ошибка компиляции! Generic array creation
       List<String>[] stringLists = new List<String>[1];
   }
}
Nanging kenapa iki ditindakake? Yagene nggawe array kasebut dilarang? Iki kabeh kanggo njamin keamanan jinis. Yen kompiler ngidini kita nggawe array kasebut saka obyek umum, kita bisa ngalami akeh masalah. 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 List<String>[] stringListsbakal diidini, lan kompiler ora bakal sambat. Mangkene apa sing bisa ditindakake ing kasus iki: Ing baris 1, kita nggawe macem-macem sheets List<String>[] stringLists. Array kita ngemot siji List<String>. Ing baris 2 kita nggawe dhaptar nomer List<Integer>. Ing baris 3 kita nemtokake array kita List<String>[]menyang variabel Object[] objects. Basa Jawa ngidini sampeyan nindakake iki: Xsampeyan bisa nglebokake obyek Xlan obyek kabeh kelas anak menyang macem-macem obyek Х. Patut, Objectssampeyan bisa nyelehake apa wae menyang array. Ing baris 4 kita ngganti unsur siji saka array objects (List<String>)karo dhaftar List<Integer>. Akibaté, kita diselehake List<Integer>ing array kita, sing mung kanggo nyimpen List<String>! Kita bakal nemoni kesalahan mung nalika kode tekan baris 5. Pangecualian bakal dibuwang sajrone eksekusi program ClassCastException. Mula, larangan nggawe susunan kasebut dienal ing basa Jawa - iki ngidini kita ngindhari kahanan kasebut.

Kepiye carane bisa ngliwati penghapusan jinis?

Ya, kita wis sinau babagan penghapusan jinis. Ayo nyoba ngapusi sistem! :) Tugas: Kita duwe kelas umum TestClass<T>. Kita kudu nggawe metode createNewT()sing bakal nggawe lan ngasilake jinis obyek anyar Т. Nanging iki ora bisa ditindakake, ta? Kabeh informasi babagan jinis Тbakal dibusak sak kompilasi, lan nalika program lagi mlaku, kita ora bakal bisa kanggo mangerteni apa jinis obyek kita kudu nggawe. Nyatane, ana siji cara sing angel. Mbokmenawa sampeyan kelingan yen ing basa Jawa ana kelas Class. Nggunakake, kita bisa njaluk kelas saka 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 salah sawijining fitur sing ora kita gunakake. Ing dokumentasi Oracle sampeyan bakal weruh yen Kelas minangka kelas umum! Busak jinis - 3Dokumentasi kasebut ujar: "T yaiku jinis kelas sing dimodelake dening obyek Kelas iki." Yen kita nerjemahake iki saka basa dokumentasi menyang basa manungsa, iki tegese kelas kanggo obyek Integer.classora mung Class, nanging Class<Integer>. Jinis obyek string.classora mung Class, Class<String>, etc. Yen isih durung jelas, coba tambahake parameter jinis menyang conto sadurunge:
public class Main2 {

   public static void main(String[] args) {

       Class<Integer> classInt = Integer.class;
       //ошибка компиляции!
       Class<String> classInt2 = Integer.class;


       Class<String> classString = String.class;
       //ошибка компиляции!
       Class<Double> classString2 = String.class;
   }
}
Lan saiki, nggunakake kawruh iki, kita bisa ngliwati jinis erasure lan ngatasi masalah kita! Ayo coba golek informasi babagan jinis parameter. Peran kasebut bakal dimainake dening kelas MySecretClass:
public class MySecretClass {

   public MySecretClass() {

       System.out.println("Объект секретного класса успешно создан!");
   }
}
Mangkene 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:

Объект секретного класса успешно создан!
Kita mung ngliwati parameter kelas sing dibutuhake menyang konstruktor kelas umum kita:
TestClass<MySecretClass> testString = new TestClass<>(MySecretClass.class);
Thanks kanggo iki, kita nyimpen informasi babagan jinis parameter lan nglindhungi supaya ora dibusak. Akibaté, kita bisa nggawe obyek T! :) Iki rampung kuliah dina iki. Pambusakan jinis mesthi kudu dieling-eling nalika nggarap generik. Iki ora katon trep banget, nanging sampeyan kudu ngerti yen generik dudu bagean saka basa Jawa nalika digawe. Iki minangka fitur sing ditambahake mengko sing mbantu nggawe koleksi sing diketik lan nyekel kesalahan ing tahap kompilasi. Sawetara basa liyane ing ngendi generik wis ana wiwit versi 1 ora duwe jinis erasure (contone, C #). Nanging, kita durung rampung sinau generik! Ing kuliah sabanjure sampeyan bakal kenal karo sawetara fitur liyane kanggo nggarap dheweke. Ing sawetoro wektu, iku bakal becik kanggo ngatasi saperangan masalah! :)
Komentar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION