JavaRush /Java блогу /Random-KY /Javaдагы генериктер деген эмне

Javaдагы генериктер деген эмне

Группада жарыяланган
Салам! Бүгүн биз генериктер жөнүндө сүйлөшөбүз. Сиз көп жаңы нерселерди үйрөнөсүз деп айтышым керек! Бул гана эмес, кийинки лекциялар да генериктерге арналат. Java-да генерик деген эмне - 1 Ошондуктан, бул тема сиз үчүн кызыктуу болсо, анда сиз бактылуусуз: бүгүн сиз генериктердин өзгөчөлүктөрү жөнүндө көп нерсени билесиз. Мейли, эгер жок болсо, тынчып, эс алыңыз! :) Бул абдан маанилүү тема жана аны бorши керек. Жөнөкөйдөн баштайлы: "эмне" жана "эмне үчүн". Генерик деген эмне? Генериктер параметри бар типтер. Жалпыны түзүүдө сиз анын түрүн гана эмес, ал менен иштөөгө тийиш болгон маалыматтардын түрүн да белгилейсиз. Менин оюмча, эң ачык мисал сиздин оюңузга келди - бул ArrayList! Бул жерде биз аны адатта программада кантип түзөбүз:
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");
   }
}
Сиз ойлогондой, тизменин өзгөчөлүгү - ага бардыгын "толтурууга" мүмкүн эмес: ал an objectтер менен гана иштейт String. Эми Java тарыхына кыскача экскурсия жасайлы жана “эмне үчүн?” деген суроого жооп берүүгө аракет кылалы. Бул үчүн, биз өзүбүз ArrayList классынын жөнөкөйлөштүрүлгөн versionсын жазабыз. Биздин тизме ички массивге гана маалыматтарды кошуп, бул маалыматтарды ала алат:
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;
   }
}
Биздин тизмеде сандар гана сакталышы керек дейли Integer. Бизде генериктер жок. IntegerТекшерүүнүн add()инстанциясын так көрсөтө албайбыз Ошондо биздин бүт классыбыз үчүн гана ылайыктуу болот Integerжана биз дүйнөдөгү бардык маалымат түрлөрү үчүн бир классты жазууга туура келет! Биз биздин программисттерге таянууну чечтик жана алар ал жерге керексиз эч нерсе кошпоо үчүн codeго комментарий калтырууну чечтик:
//use it ONLY with Integer data type
public void add(Object o) {
   this.data[count] = o;
   count++;
}
Программисттердин бири бул комментарийди байкабай калып, тизмеге саптар аралаш сандарды киргизип, анан алардын суммасын эсептөөгө аракет кылган:
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);
   }
}
Консолдун чыгышы: 300 "main" жипиндеги өзгөчөлүк java.lang.ClassCastException: java.lang.String Main.main (Main.java:14) дарегинде java.lang.Integerге чыгарылbyte. Бул кырдаалда эң жаманы эмне? Программисттин көңүл коштугунан алыс. Эң жаманы, туура эмес code биздин программада маанилүү орунда калып, ийгorктүү түзүлгөн . Эми биз катаны codeдоо стадиясында эмес, тестирлөө баскычында гана көрөбүз (жана бул эң жакшы учурда!). Иштеп чыгууда мүчүлүштүктөрдү оңдоо бир топ кымбатка турат — акча да, убакыт да. Бул так генериктердин артыкчылыгы: генерикалык класс бактысыз программистке катаны дароо аныктоого мүмкүндүк берет. Код жөн эле түзүлбөйт!
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!
   }
}
Программист дароо «эсине келип», өзүн заматта оңдойт. Баса, Listмындай катаны көрүү үчүн өзүбүздүн классыбызды түзүүнүн кереги жок болчу. <Integer>Кадимки 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));
   }
}
Консолдун чыгышы: 300 "main" жиптеги өзгөчөлүк java.lang.ClassCastException: java.lang.String Main.main(Main.java:16) боюнча java.lang.Integerге чыгарууга болбойт. Java, сиз бул катаны кетирип, кооптуу коллекция түзө аласыз. Однако, если вставить этот code в IDEa, мы увидим предупреждение: “ Unchecked call to add(E) as a member of raw type of java.util.List ” Нам подсказывают, что при добавлении element в коллекцию без дженериков что-то может пойти андай эмес. Бирок "чийки түрү" деген сөз эмнени билдирет? Сөзмө-сөз котормо абдан так болот - " чийки түрү " же " кир түрү ". Raw typeанын түрү алынып салынган жалпы класс болуп саналат. Башкача айтканда, List myList1бул Raw type. Тескерисинче raw type, типтин спецификациясы менен туура түзүлгөн generic typeжалпы класс (класс катары да белгилүү ). parameterized typeМисалы, List<String> myList1. Сизде суроо пайда болушу мүмкүн: эмне үчүн колдонууга уруксат берилген raw types? Себеби жөнөкөй. raw typesJava түзүүчүлөрү шайкештик көйгөйлөрүн жаратпоо үчүн тилде колдоо калтырышкан . Java 5.0 чыкканга чейин (генериктери бул versionда биринчи жолу пайда болгон), көп code мурунтан эле raw types. Ошондуктан, бул мүмкүнчүлүк бүгүнкү күндө дагы бар. Жошуа Блохтун классикалык китеби «Эффективдүү Java» жөнүндө биз буга чейин лекцияларда бир нече жолу айтканбыз. Тилди жаратуучулардын бири катары ал китепте колдонуу темасын да көз жаздымда калтырган raw typesэмес generic types. Java-да генерик деген эмне - 2Бул китептин 23-бөлүмүндө өтө көркөм аталыш бар: “Жаңы codeдо чийки түрлөрүн колдонбоңуз.” Бул нерсени эстен чыгарбашыңыз керек. Жалпы класстарды колдонгондо, аларды эч generic typeкачан raw type.

Терилген методдор

Java жалпы методдор деп аталгандарды түзүп, жеке ыкмаларды терүүгө мүмкүндүк берет. Эмне үчүн мындай ыкмалар ыңгайлуу? Биринчиден, алар ар кандай типтеги параметрлер менен иштөөгө мүмкүндүк берет. Эгерде бир эле логиканы ар кандай түрлөргө колдонууга мүмкүн болсо, жалпы ыкма сонун чечим болуп саналат. Келгиле, бир мисал карап көрөлү. Бизде кандайдыр бир тизме бар дейли myList1. Биз андан бардык баалуулуктарды алып салгыбыз келет жана бардык бош орундарды жаңы маани менен толтургубуз келет. Жалпы ыкма менен классыбыз ушундай болот:
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("Старая строка 1");
       strings.add("Старая строка 2");
       strings.add("Старая строка 3");

       fill(strings, "Новая строка");

       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);
   }
}
Синтаксиске көңүл буруңуз, ал бир аз адаттан тыш көрүнөт:
public static <T> void fill(List<T> list, T val)
Кайтаруу түрүнүн алдында <T> турат, ал жалпы ыкманы көрсөтөт. Бул учурда, ыкма киргизүү катары 2 параметрди кабыл алат: T an objectтеринин тизмеси жана башка өзүнчө an object T. <T> колдонуу менен методду терүүгө жетишилет: биз саптардын тизмесин жана ал жерге санды өткөрө албайбыз. Саптардын жана саптардын тизмеси, сандардын жана сандардын тизмеси, биздин an objectтердин тизмеси Catжана башка an object Cat- бул жалгыз жол. Метод ар кандай типтеги маалыматтар менен оңой иштээрин main()ачык көрсөтүп турат . fill()Биринчиден, ал саптардын жана саптардын тизмесин, андан кийин сандардын жана сандардын тизмесин киргизүү катары кабыл алат. Консолдун чыгышы: [Newline, Newline, Newline] [888, 888, 888] Элестеткиле, fill()бизге 30 түрдүү класстар үчүн метод логикасы керек болсо жана бизде жалпы методдор жок болсо керек. Биз бир эле ыкманы 30 жолу жазууга аргасыз болобуз, ар кандай маалымат түрлөрү үчүн! Бирок жалпы ыкмалардын аркасында биз codeубузду кайра колдоно алабыз! :)

Терилген класстар

Сиз Java-да берилген жалпы класстарды гана колдонбостон, өзүңүздүн классыңызды да түзө аласыз! Бул жерде жөнөкөй мисал:
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("Старая строка");
       System.out.println(stringBox.get());
       stringBox.set("Новая строка");

       System.out.println(stringBox.get());

       stringBox.set(12345);//ошибка компиляции!
   }
}
Биздин класс Box<T>(«куту») машинкада. Түзүү учурунда ага маалымат түрүн ( ) ыйгаруу менен <T>, биз ага башка типтеги an objectтерди жайгаштыра албайбыз. Муну мисалдан көрүүгө болот. Түзүүдө биздин an object саптар менен иштей турганын белгилегенбиз:
Box<String> stringBox = new Box<>();
Ал эми codeдун акыркы сабында кутучанын ичине 12345 санын коюуга аракет кылганда, компиляция катасын алабыз! Ушундай эле, биз өзүбүздүн жалпы классыбызды түздүк! :) Ушуну менен бүгүнкү лекциябыз аяктайт. Бирок биз генериктер менен коштошпойбуз! Кийинки лекцияларда биз дагы өркүндөтүлгөн функциялар жөнүндө сүйлөшөбүз, андыктан коштошпоңуз! ) Окууңарга ийгorк! :)
Комментарийлер
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION