CodeGym /Java Blog /Random /Generics sa Java
John Squirrels
Antas
San Francisco

Generics sa Java

Nai-publish sa grupo
Hi! Pag-uusapan natin ang tungkol sa Java Generics. Dapat kong sabihin na marami kang matututunan! Hindi lamang ang araling ito, kundi pati na rin ang mga susunod na aralin, ay ilalaan sa generics. Kaya, kung interesado ka sa generics, ngayon ay masuwerteng araw ka: marami kang matututunan tungkol sa mga feature ng generics. At kung hindi, magbitiw sa iyong sarili at magpahinga! :) Ito ay isang napakahalagang paksa, at kailangan mong malaman ito. Magsimula tayo sa simpleng: ang "ano" at ang "bakit".

Ano ang Java Generics?

Ang mga generic ay mga uri na may parameter. Kapag gumagawa ng generic na uri, tinukoy mo hindi lamang isang uri, kundi pati na rin ang uri ng data kung saan gagana ito. Sa palagay ko ang pinaka-halatang halimbawa ay pumasok na sa iyong isip: ArrayList! Ganito kami karaniwang gumagawa ng isa sa isang programa:

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");
   }
}
Tulad ng maaari mong hulaan, isang tampok ng listahang ito ay hindi namin mailalagay ang lahat dito: eksklusibo itong gumagana sa mga String object. Ngayon ay kumuha tayo ng kaunting paglihis sa kasaysayan ng Java at subukang sagutin ang tanong na "bakit?" Para magawa ito, isusulat namin ang sarili naming pinasimpleng bersyon ng klase ng ArrayList. Alam lang ng aming listahan kung paano magdagdag ng data at kumuha ng data mula sa isang panloob na hanay:

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;
   }
}
Ipagpalagay na gusto naming ang aming listahan ay mag-imbak lamang ng Integer s. Hindi kami gumagamit ng generic na uri. Hindi namin gustong magsama ng isang tahasang "instanceof Integer " check sa add() na paraan. Kung gagawin namin, ang aming buong klase ay magiging angkop lamang para sa Integer , at kailangan naming magsulat ng katulad na klase para sa bawat iba pang uri ng data sa mundo! Aasa kami sa aming mga programmer, at mag-iwan lamang ng komento sa code upang matiyak na hindi sila nagdaragdag ng anumang bagay na hindi namin gusto:

// Use this class ONLY with the Integer data type
public void add(Object o) {
   this.data[count] = o;
   count++;
}
Hindi sinasadya ng isa sa mga programmer ang komentong ito at hindi sinasadyang naglagay ng ilang Strings sa isang listahan ng mga numero at pagkatapos ay kinakalkula ang kanilang kabuuan:

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);
   }
}
Output ng console:

300 
Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer 
      at Main.main (Main.java:14)
Ano ang pinakamasamang bahagi ng sitwasyong ito? Tiyak na hindi ang kawalang-ingat ng programmer. Ang pinakamasamang bahagi ay ang hindi tamang code ay napunta sa isang mahalagang lugar sa aming programa at matagumpay na naipon. Ngayon ay makakatagpo tayo ng bug hindi habang nagsusulat ng code, ngunit sa panahon lamang ng pagsubok (at ito ang pinakamagandang sitwasyon ng kaso!). Ang pag-aayos ng mga bug sa mga susunod na yugto ng pag-unlad ay nagkakahalaga ng higit pa — parehong sa mga tuntunin ng pera at oras. Ito ay tiyak kung saan ang mga generic ay nakikinabang sa atin: ang isang generic na klase ay nagbibigay-daan sa malas na programmer na makita ang error kaagad. Ang programa ay hindi lamang mag-compile!

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!
   }
}
Agad na napagtanto ng programmer ang kanyang pagkakamali at agad na bumubuti. Sa pamamagitan ng paraan, hindi namin kailangang lumikha ng aming sariling klase ng Listahan upang makita ang ganitong uri ng error. Tanggalin lang ang mga angle bracket at i-type ang ( <Integer> ) mula sa isang ordinaryong ArrayList!

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));
   }
}
Output ng console:

300 
Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer 
     at Main.main(Main.java:16)
Sa madaling salita, kahit na gamit ang mga "katutubong" mekanismo ng Java, maaari tayong gumawa ng ganitong uri ng pagkakamali at lumikha ng hindi ligtas na koleksyon. Gayunpaman, kung i-paste namin ang code na ito sa isang IDE, makakatanggap kami ng babala: "Hindi naka-check na tawag para idagdag ang(E) bilang miyembro ng raw na uri ng java.util.List" Sinabihan kami na may maaaring magkamali kapag nagdaragdag ng item sa isang koleksyon na walang generic na uri. Ngunit ano ang ibig sabihin ng pariralang "hilaw na uri"? Ang isang raw na uri ay isang generic na klase na ang uri ay inalis. Sa madaling salita, ang List myList1 ay isang raw na uri . Ang kabaligtaran ng isang raw na uri ay isang generic na uri — isang generic na klase na may indikasyon ng (mga) parameterized na uri . Halimbawa, List<String> myList1. Maaari mong itanong kung bakit pinapayagan ng wika ang paggamit ng mga hilaw na uri ? Simple lang ang dahilan. Ang mga tagalikha ng Java ay nag-iwan ng suporta para sa mga hilaw na uri sa wika upang maiwasan ang paglikha ng mga problema sa compatibility. Sa oras na inilabas ang Java 5.0 (unang lumabas ang mga generic sa bersyong ito), marami nang code ang naisulat na gamit ang mga hilaw na uri . Bilang resulta, ang mekanismong ito ay sinusuportahan pa rin ngayon. Paulit-ulit naming binanggit ang klasikong aklat ni Joshua Bloch na "Effective Java" sa mga aralin. Bilang isa sa mga lumikha ng wika, hindi niya nilaktawan ang mga hilaw na uri at generic na uri sa kanyang aklat. Ano ang mga generic sa Java?  - 2Ang Kabanata 23 ng aklat ay may napakahusay na pamagat: "Huwag gumamit ng mga hilaw na uri sa bagong code" Ito ang kailangan mong tandaan. Kapag gumagamit ng mga generic na klase, huwag kailanman gawing raw na uri ang isang generic na uri .

Mga generic na pamamaraan

Hinahayaan ka ng Java na i-parameter ang mga indibidwal na pamamaraan sa pamamagitan ng paggawa ng tinatawag na mga generic na pamamaraan. Paano nakatutulong ang mga ganitong paraan? Higit sa lahat, nakakatulong ang mga ito dahil hinahayaan ka nitong magtrabaho sa iba't ibang uri ng mga parameter ng pamamaraan. Kung ang parehong lohika ay maaaring ligtas na mailapat sa iba't ibang uri, ang isang generic na pamamaraan ay maaaring maging isang mahusay na solusyon. Isaalang-alang ito ng isang napakasimpleng halimbawa: Ipagpalagay na mayroon kaming ilang listahan na tinatawag na myList1 . Gusto naming tanggalin ang lahat ng value sa listahan at punan ang lahat ng bakanteng espasyo ng mga bagong value. Narito kung ano ang hitsura ng aming klase sa isang generic na pamamaraan:

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);
   }
}
Bigyang-pansin ang syntax. Mukhang medyo hindi karaniwan:

public static <T> void fill(List<T> list, T val)
Sinusulat namin ang <T> bago ang uri ng pagbabalik. Ipinapahiwatig nito na nakikipag-usap tayo sa isang pangkaraniwang pamamaraan. Sa kasong ito, ang pamamaraan ay tumatanggap ng 2 parameter bilang isang input: isang listahan ng mga T object at isa pang hiwalay na T object. Sa pamamagitan ng paggamit ng <T>, sinusuri namin ang mga uri ng parameter ng pamamaraan: hindi kami makapasa sa isang listahan ng Strings at isang Integer. Isang listahan ng mga Strings at isang String, isang listahan ng mga Integer at isang Integer, isang listahan ng sarili nating mga object ng Cat at isa pang Cat object — iyon ang kailangan nating gawin. Ang pangunahing() na pamamaraan ay naglalarawan kung paano ang fill() na pamamaraan ay madaling magamit upang gumana sa iba't ibang uri ng data. Una, ginagamit namin ang pamamaraan na may isang listahan ng mga String at isang String bilang input, at pagkatapos ay may isang listahan ng mga Integer at isang Integer. Output ng console:

[New String, New String, New String] [888, 888, 888]
Isipin kung wala kaming mga generic na pamamaraan at kailangan ang lohika ng fill() na pamamaraan para sa 30 iba't ibang klase. Kailangan nating isulat ang parehong paraan ng 30 beses para sa iba't ibang uri ng data! Ngunit salamat sa mga generic na pamamaraan, maaari naming muling gamitin ang aming code! :)

Mga generic na klase

Hindi ka limitado sa mga generic na klase na ibinigay sa karaniwang mga library ng Java — maaari kang lumikha ng iyong sarili! Narito ang isang simpleng halimbawa:

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!
   }
}
Ang aming Box<T> class ay isang generic na klase. Sa sandaling magtalaga kami ng uri ng data ( <T> ) sa panahon ng paglikha, hindi na namin magagawang maglagay ng mga bagay na may iba pang uri dito. Ito ay makikita sa halimbawa. Kapag nililikha ang aming object, ipinahiwatig namin na gagana ito sa Strings:

Box<String> stringBox = new Box<>();
At sa huling linya ng code, kapag sinubukan naming ilagay ang numerong 12345 sa loob ng kahon, nakakakuha kami ng error sa compilation! Ganun lang kadali! Gumawa kami ng sarili naming generic na klase! :) With that, natapos na ang lesson ngayon. Ngunit hindi kami nagpapaalam sa mga generics! Sa susunod na mga aralin, pag-uusapan natin ang tungkol sa mas advanced na mga tampok, kaya huwag kang umalis! ) Upang palakasin ang iyong natutunan, iminumungkahi naming manood ka ng isang video lesson mula sa aming Java Course
Pinakamahusay na tagumpay sa iyong pag-aaral! :)
Mga komento
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION