JavaRush /Блоги Java /Random-TG /Назарияи генерикҳо дар Java ё чӣ гуна гузоштани қавс дар ...
Viacheslav
Сатҳи

Назарияи генерикҳо дар Java ё чӣ гуна гузоштани қавс дар амал

Дар гурӯҳ нашр шудааст

Муқаддима

Аз JSE 5.0 сар карда, генерикҳо ба арсенали забони Java илова карда шуданд.
Назарияи генерикҳо дар Java ё тарзи гузоштани қавс дар амал - 1

Генерикҳо дар Java чист?

Генерикҳо (умумкунӣ) воситаи махсуси забони Java барои татбиқи барномасозии умумӣ мебошанд: равиши махсус барои тавсифи додаҳо ва алгоритмҳо, ки ба шумо имкон медиҳад бо намудҳои гуногуни додаҳо бидуни тағир додани тавсифи онҳо кор кунед. Дар вебсайти Oracle, як дарси алоҳида ба генерикҳо бахшида шудааст: " Дарс: Generics ".

Аввалан, барои фаҳмидани генерикҳо, шумо бояд фаҳмед, ки чаро онҳо умуман лозиманд ва онҳо чӣ пешниҳод мекунанд. Дар дарсӣ дар бахши " Чаро истифода аз генерикҳо ?" Гуфта мешавад, ки яке аз ҳадафҳо қавитар санҷиши навъи компилятсионӣ ва аз байн бурдани зарурати кастинги возеҳ аст.
Назарияи генерикҳо дар Java ё тарзи гузоштани қавс дар амал - 2
Биёед дарсҳои дӯстдоштаи худро барои таҷрибаҳо омода созем . Биёед ин codeро тасаввур кунем:
import java.util.*;
public class HelloWorld{
	public static void main(String []args){
		List list = new ArrayList();
		list.add("Hello");
		String text = list.get(0) + ", world!";
		System.out.print(text);
	}
}
Ин code хуб кор хоҳад кард. Аммо агар ба назди мо омада, гӯянд, ки ибораи «Салом, ҷаҳон!». латукӯб ва шумо танҳо метавонед баргаштан Салом? Биёед пайвандро бо сатр аз code хориҷ кунем ", world!". Чунин ба назар мерасад, ки чӣ метавонад безарартар бошад? Аммо дар асл, мо Хангоми тартиб додани хато хато мегирем : error: incompatible types: Object cannot be converted to String Гап дар он аст, ки дар ҳолати мо Рӯйхат рӯйхати an objectҳои навъи Objectро нигоҳ медорад. Азбаски String насли Объект аст (азбаски ҳама синфҳо аз Object дар Java ба таври ғайримустақим ба мерос гирифта шудаанд), он кассаи возеҳро талаб мекунад, ки мо ин корро накардаем. Ва ҳангоми пайвастшавӣ, усули статикии String.valueOf(obj) дар an object даъват карда мешавад, ки дар ниҳоят усули toString -ро дар Объект даъват мекунад. Яъне Рӯйхати мо Объектро дар бар мегирад. Маълум мешавад, ки дар он ҷое, ки ба мо намуди мушаххас лозим аст, на Объект, мо бояд худамон кастингро анҷом диҳем:
import java.util.*;
public class HelloWorld{
	public static void main(String []args){
		List list = new ArrayList();
		list.add("Hello!");
		list.add(123);
		for (Object str : list) {
		    System.out.println((String)str);
		}
	}
}
Аммо, дар ин маврид, зеро Рӯйхат рӯйхати an objectҳоро қабул мекунад, он на танҳо String, балки Integerро низ нигоҳ медорад. Аммо бадтарин чиз он аст, ки дар ин сурат компилятор ягон чизи нодурустро намебинад. Ва дар ин ҷо мо дар ВАҚТИ ИҶРО хато мегирем (онҳо инчунин мегӯянд, ки хатогӣ "дар Runtime" гирифта шудааст). Хатогӣ чунин хоҳад буд: java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String розӣ шавед, на аз ҳама гуворо. Ва ҳамаи ин аз он иборат аст, ки компилятор зеҳни сунъӣ нест ва он чизеро, ки барномасоз маънои онро дорад, тахмин зада наметавонад. Барои ба компилятор дар бораи он ки мо кадом намудҳоро истифода мебарем, бештар маълумот диҳад, Java SE 5 генерикҳоро муаррифӣ кард . Биёед versionи худро ислоҳ карда, ба компилятор чизеро, ки мо мехоҳем, бигӯем:
import java.util.*;
public class HelloWorld {
	public static void main(String []args){
		List<String> list = new ArrayList<>();
		list.add("Hello!");
		list.add(123);
		for (Object str : list) {
		    System.out.println(str);
		}
	}
}
Тавре ки мо мебинем, мо дигар ба cast ба String ниёз надорем. Илова бар ин, мо ҳоло қавсҳои кунҷӣ дорем, ки генерикҳоро чаҳорчӯба мекунанд. Акнун компилятор имкон намедиҳад, ки синф тартиб дода шавад, то даме ки мо иловаи 123-ро ба рӯйхат хориҷ накунем, зеро ин Integer аст. Ӯ ба мо чунин хоҳад гуфт. Бисёр одамон генерикҳоро "шакари синтаксисӣ" меноманд. Ва онҳо дурустанд, зеро генерикҳо ҳангоми тартиб додан воқеан ҳамон кастаҳо хоҳанд шуд. Биёед ба bytecodeи синфҳои тартибдода назар андозем: бо рехтани дастӣ ва истифодаи генерикҳо:
Назарияи генерикӣ дар Java ё тарзи гузоштани қавс дар амал - 3
Пас аз тартиб додан, ҳама гуна маълумот дар бораи генерикҳо нест карда мешаванд. Ин "Type Erasure" ё " Type Erasure " номида мешавад . Намудҳои тозакунӣ ва генерикҳо барои таъмини мутобиқати ақиб бо versionҳои кӯҳнаи JDK тарҳрезӣ шудаанд ва ҳамзамон ба компилятор имкон медиҳанд, ки дар versionҳои нави Java дар хулосабарории намуд кӯмак кунанд.
Назарияи генерикӣ дар Java ё тарзи гузоштани қавс дар амал - 4

Намудҳои хом ё навъҳои хом

Ҳангоми сухан дар бораи генерикҳо, мо ҳамеша ду категория дорем: намудҳои чопшуда (Намудҳои умумӣ) ва намудҳои “хом” (Намудҳои хом). Навъҳои хом навъҳое мебошанд, ки бидуни нишон додани "тахассус" дар қавсҳои кунҷӣ:
Назарияи генерикҳо дар Java ё тарзи гузоштани қавс дар амал - 5
Навъҳои чопшуда баръакс бо ишораи "равшанкунӣ" мебошанд:
Назарияи генерикҳо дар Java ё тарзи гузоштани қавс дар амал - 6
Тавре ки мо мебинем, мо тарҳи ғайриоддӣ истифода бурдем, ки бо тирча дар скриншот нишон дода шудааст. Ин як синтаксиси махсусест, ки дар Java SE 7 илова карда шудааст ва онро " алмос " меноманд , ки маънои алмосро дорад. Чаро? Шумо метавонед байни шакли алмос ва шакли қавсҳои ҷингила муқоиса кунед: <> Синтаксиси алмос инчунин бо мафҳуми " Намуди хулоса " ё хулосаи навъи алоқаманд аст. Дар ниҳоят, компилятор <>-ро дар тарафи рост дида, ба тарафи чап нигоҳ мекунад, ки дар он эъломияи навъи тағирёбанда, ки ба он қимат дода шудааст, ҷойгир аст. Ва аз ин ќисмат вай мефањмад, ки ќимати дар тарафи рост чї хел навишта шудааст. Дарвоқеъ, агар умумӣ дар тарафи чап нишон дода шуда бошад ва дар тарафи рост нишон дода нашавад, компилятор метавонад намуди:
import java.util.*;
public class HelloWorld{
	public static void main(String []args) {
		List<String> list = new ArrayList();
		list.add("Hello World");
		String data = list.get(0);
		System.out.println(data);
	}
}
Аммо, ин омехтаи услуби нав бо генерикҳо ва услуби кӯҳна бидуни онҳо хоҳад буд. Ва ин хеле номатлуб аст. Ҳангоми тартиб додани codeи дар боло овардашуда мо паёмро мегирем: Note: HelloWorld.java uses unchecked or unsafe operations. Дарвоқеъ, маълум нест, ки чаро умуман ба ин ҷо алмос илова кардан лозим аст. Аммо инак як мисол:
import java.util.*;
public class HelloWorld{
	public static void main(String []args) {
		List<String> list = Arrays.asList("Hello", "World");
		List<Integer> data = new ArrayList(list);
		Integer intNumber = data.get(0);
		System.out.println(data);
	}
}
Тавре ки мо дар хотир дорем, ArrayList инчунин як конструктори дуюм дорад, ки коллексияро ҳамчун вуруд мегирад. Ва дар ин ҷо фиреб аст. Бе синтаксиси алмос, мураттиб намефаҳмад, ки фиреб дода мешавад, аммо бо алмос мефаҳмад. Аз ин рӯ, қоидаи №1 : ҳамеша синтаксиси алмосро истифода баред, агар мо намудҳои чопшударо истифода барем. Дар акси ҳол, мо хавфи гум кардани он ҷое, ки навъи хомро истифода мебарем. Барои роҳ надодан ба огоҳиҳо дар гузориш, ки "амалҳои тафтишнашуда ё хатарнокро истифода мебарад", шумо метавонед дар бораи усул ё синфи истифодашаванда тавзеҳи махсусро муайян кунед: @SuppressWarnings("unchecked") Паҳнкунӣ ҳамчун супурд тарҷума карда мешавад, яъне айнан, барои пахш кардани огоҳиҳо. Аммо фикр кунед, ки чаро шумо тасмим гирифтед, ки онро нишон диҳед? Қоидаи рақами якро дар хотир доред ва шояд ба шумо лозим аст, ки чопкуниро илова кунед.
Назарияи генерикҳо дар Java ё тарзи гузоштани қавс дар амал - 7

Усулҳои умумӣ

Генерикҳо ба шумо имкон медиҳанд, ки усулҳоро чоп кунед. Дар дастури Oracle бахши алоҳида ба ин хусусият бахшида шудааст: “ Усулҳои умумӣ ”. Аз ин дарс муҳим аст, ки синтаксисро дар хотир дошта бошед:
  • рӯйхати параметрҳои чопшударо дар дохor қавсҳои кунҷӣ дар бар мегирад;
  • рӯйхати параметрҳои чопшуда пеш аз усули баргардонида мешавад.
Биёед як мисолро дида бароем:
import java.util.*;
public class HelloWorld{

    public static class Util {
        public static <T> T getValue(Object obj, Class<T> clazz) {
            return (T) obj;
        }
        public static <T> T getValue(Object obj) {
            return (T) obj;
        }
    }

    public static void main(String []args) {
		List list = Arrays.asList("Author", "Book");
		for (Object element : list) {
		    String data = Util.getValue(element, String.class);
		    System.out.println(data);
		    System.out.println(Util.<String>getValue(element));
		}
    }
}
Агар шумо ба синфи Util нигаред, мо дар он ду усули чопшударо мебинем. Бо хулосаи намуд, мо метавонем таърифи намудро мустақиман ба компилятор пешниҳод кунем ё худамон онро муайян кунем. Ҳарду вариант дар мисол оварда шудаанд. Дар омади гап, агар шумо дар ин бора фикр кунед, синтаксис комилан мантиқист. Ҳангоми чоп кардани усул, мо генерикиро ПЕШ АЗ метод муайян мекунем, зеро агар мо баъд аз усул генералиро истифода барем, Java наметавонад муайян кунад, ки кадом навъи онро истифода бурдан лозим аст. Аз ин рӯ, мо аввал эълон мекунем, ки мо T-и генералиро истифода мебарем ва баъд мегӯем, ки ин генералиро бармегардонем. Табиист, ки Util.<Integer>getValue(element, String.class)он бо хатогӣ ноком мешавад incompatible types: Class<String> cannot be converted to Class<Integer>. Ҳангоми истифодаи усулҳои чопшуда, шумо бояд ҳамеша дар бораи тозакунии навъи тип дар хотир доред. Биёед як мисолро дида бароем:
import java.util.*;
public class HelloWorld {

    public static class Util {
        public static <T> T getValue(Object obj) {
            return (T) obj;
        }
    }

    public static void main(String []args) {
		List list = Arrays.asList(2, 3);
		for (Object element : list) {
		    System.out.println(Util.<Integer>getValue(element) + 1);
		}
    }
}
Ин бузург кор хоҳад кард. Аммо танҳо то он даме, ки компилятор дарк мекунад, ки усули даъватшуда дорои навъи Integer мебошад. Биёед баромади консолро бо сатри зерин иваз кунем: System.out.println(Util.getValue(element) + 1); Ва мо хато мегирем: намудҳои операндҳои бад барои оператори бинарӣ '+', навъи якум: Объект , навъи дуюм: int Яъне, намудҳо тоза карда шудаанд. Компилятор мебинад, ки ҳеҷ кас намудро муайян накардааст, навъи он ҳамчун Объект муайян карда мешавад ва иҷрои code бо хатогӣ ноком мешавад.
Назарияи генерикҳо дар Java ё тарзи гузоштани қавс дар амал - 8

Намудҳои умумӣ

Шумо метавонед на танҳо усулҳоро, балки худи синфҳоро низ нависед. Oracle дар дастури худ бахши " Намудҳои умумӣ " дорад, ки ба ин бахшида шудааст. Биёед як мисолро дида бароем:
public static class SomeType<T> {
	public <E> void test(Collection<E> collection) {
		for (E element : collection) {
			System.out.println(element);
		}
	}
	public void test(List<Integer> collection) {
		for (Integer element : collection) {
			System.out.println(element);
		}
	}
}
Дар ин ҷо ҳама чиз оддӣ аст. Агар мо синфро истифода барем, умумӣ пас аз номи синф номбар карда мешавад. Биёед ҳоло як мисоли ин синфро дар усули асосӣ эҷод кунем:
public static void main(String []args) {
	SomeType<String> st = new SomeType<>();
	List<String> list = Arrays.asList("test");
	st.test(list);
}
Он хуб кор хоҳад кард. Компилятор мебинад, ки Рӯйхати рақамҳо ва Маҷмӯаи навъи String мавҷуд аст. Аммо чӣ мешавад, агар мо генерикҳоро нест кунем ва ин корро кунем:
SomeType st = new SomeType();
List<String> list = Arrays.asList("test");
st.test(list);
Мо хатогиро мегирем: java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer Боз аз нав ворид кардани тозакунӣ. Азбаски синф дигар умумӣ надорад, компилятор қарор мекунад, ки азбаски мо Рӯйхатро гузаронидем, усули бо Рӯйхати<Бутун> мувофиқтар аст. Ва мо бо хато меафтем. Аз ин рӯ, қоидаи №2: Агар синф чоп шуда бошад, ҳамеша навъи онро дар generic муайян кунед .

Маҳдудиятҳо

Мо метавонем ба навъҳои дар генерикӣ нишондодашуда маҳдудият татбиқ кунем. Масалан, мо мехоҳем, ки контейнер танҳо Рақамро ҳамчун вуруд қабул кунад. Ин хусусият дар дастури Oracle дар бахши Параметрҳои навъи маҳдуд тавсиф шудааст . Биёед як мисолро дида бароем:
import java.util.*;
public class HelloWorld{

    public static class NumberContainer<T extends Number> {
        private T number;

        public NumberContainer(T number)  { this.number = number; }

        public void print() {
            System.out.println(number);
        }
    }

    public static void main(String []args) {
		NumberContainer number1 = new NumberContainer(2L);
		NumberContainer number2 = new NumberContainer(1);
		NumberContainer number3 = new NumberContainer("f");
    }
}
Тавре ки шумо мебинед, мо навъи умумиро маҳдуд кардем, то синфи Рақам/интерфейс ва насли он бошад. Ҷолиб он аст, ки шумо метавонед на танҳо синф, балки интерфейсҳоро низ муайян кунед. Масалан: public static class NumberContainer<T extends Number & Comparable> { Generics инчунин консепсияи Wildcard доранд https://docs.oracle.com/javase/tutorial/java/generics/wildcards.html Онҳо дар навбати худ ба се намуд тақсим мешаванд:
  • Аломатҳои ҷобаҷогузории болоӣ - < ? Рақам > васеъ мекунад
  • Рамзҳои ҷории бемаҳдуд - < ? >
  • Аломатҳои ҷобаҷогузории поёнӣ - < ? super Integer >
Принсипи ба истилоҳ Get Put ба Wildcards дахл дорад . Онҳоро дар шакли зерин ифода кардан мумкин аст:
Назарияи генерикҳо дар Java ё тарзи гузоштани қавс дар амал - 9
Ин принсипро принсипи PECS (Producer Extends Consumer Super) низ меноманд. Шумо метавонед маълумоти бештарро дар бораи Habré дар мақолаи " Истифодаи аломатҳои ҷонишинӣ барои беҳтар кардани коршоямии Java API " ва инчунин дар муҳокимаи аъло дар бораи stackoverflow: " Истифодаи аломатҳои ҷодугарӣ дар Generics Java " хонед. Ин аст як мисоли хурд аз манбаи Java - усули Collections.copy:
Назарияи генерикҳо дар Java ё тарзи гузоштани қавс дар амал - 10
Хуб, як мисоли хурде, ки чӣ тавр он кор намекунад:
public static class TestClass {
	public static void print(List<? extends String> list) {
		list.add("Hello World!");
		System.out.println(list.get(0));
	}
}

public static void main(String []args) {
	List<String> list = new ArrayList<>();
	TestClass.print(list);
}
Аммо агар шумо васеъшавиро бо супер иваз кунед, ҳама чиз хуб мешавад. Азбаски мо пеш аз баровардани он рӯйхатро бо арзиш пур мекунем, он барои мо истеъмолкунанда, яъне истеъмолкунанда аст. Аз ин рӯ, мо суперро истифода мебарем.

Мерос

Боз як хусусияти ғайриоддии генерикҳо вуҷуд дорад - мероси онҳо. Мероси генерикҳо дар дастури Oracle дар бахши " Генерикҳо, мерос ва зернамудҳо " тавсиф шудааст. Чизи асосиаш ин аст, ки дар хотир доштан ва дарк кардани чизҳои зерин. Мо ин корро карда наметавонем:
List<CharSequence> list1 = new ArrayList<String>();
Азбаски мерос бо генерикҳо гуногун кор мекунад:
Назарияи генерикҳо дар Java ё тарзи гузоштани қавс дар амал - 11
Ва инак боз як мисоли хубе ҳаст, ки бо хатогӣ ноком мешавад:
List<String> list1 = new ArrayList<>();
List<Object> list2 = list1;
Дар ин ҷо ҳам ҳама чиз оддӣ аст. List<String> насли List<Object> нест, гарчанде ки String насли Объект аст.

Финал

Ҳамин тавр, мо хотираи генерикҳои худро нав кардем. Агар онҳо дар тамоми қувваи худ хеле кам истифода шаванд, баъзе ҷузъиёт аз хотира меафтад. Умедворам, ки ин баррасии кӯтоҳ хотираи шуморо тоза мекунад. Ва барои натиҷаҳои бештар, ман ба шумо тавсия медиҳам, ки маводи зеринро хонед: #Вячеслав
Шарҳҳо
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION