Вступ
В JDK 5.0 з’явилось декілька нових розширень до мови програмування Java. Одне з таких нововведень – це узагальнення (generics).
Ви можливо вже знайомі з подібними конструкціями з інших мов, найімовірніше з шаблонами (templates) в С++. Якщо так, то ви побачите, що є як подібності так і важливі відмінності. Якщо ви незнайомі з подібними конструкціями звідкілясь, це на краще, ви можете розпочати вивчення з нуля без помилкових уявлень.
Узагальнення дозволяють вам абстрагуватися над типами. Найбільш загальним прикладом є контейнерні типи, такі як у ієрархії Колекцій.
Ось типовий приклад даного виду:
Приведення в рядку 3 є злегка надокучливим. Зазвичай, програміст знає, який тип даних поміщено у відповідний список. Проте, приведення є важливим. Компілятор може лише гарантувати, що ітератор поверне Object. Щоб забезпечити коректне присвоєння змінній типу Integer, необхідне приведення до типу.
Звичайно, приведення не тільки призводить до надмірності в коді. Це також призводить до можливостей помилок часу виконання, тож програма буде неправильною.
А що якщо програміст зможе вказати свої справжні наміри і задати список з конкретними обмеженнями щодо типів даних для розміщення у ньому? Це основна ідея узагальнень. Ось версія програмного фрагменту даного вище з використанням узагальнень:
Запримітьте оголошення типу для змінної myIntList. Написавши List<Integer>, зазначаємо, що це не довільний список, а цілочисельний список. Ми говоримо, що List є узагальненим інтерфейсом, що отримує параметр типу – в даному випадку, Integer. Ми також зазначаємо параметр типу, коли створюємо об’єкт списку.
Зазначте, також, що приведення в рядку 3 відкинуто. Зараз, ви мабуть думаєте, що ми перетасували безладдя в коді . Замість приведення до Integer в рядку 3, ми маємо Integer як параметр типу в рядку 1. Проте, тут є значна велика відмінність. Тепер компілятор може провірити коректність типів в програмі під час компіляції. Коли ми кажемо, що myIntList є оголошеним з типом List<Integer>, це говорить нам дещо про змінну myIntList, яка міститеми правильні дані, коли б змінна не застосовувалась і компілятор гарантуватиме це. А приведення ж нам говорить інше, що програміст думає, що це вірно в даній точці коду.
В результаті ж, особливо в великих програмах, це покращує читабельність та робастність (правильну роботу) програми.
Визначення простого узагальнення
Ось невеликий витяг з визначень інтерфейсів List та Iterator в пакеті java.util:
Усе це повинне бути зрозуміле вам, окрім вмісту в кутових дужках. Це – оголошення формального параметру типу інтерфейсів List and Iterator.
Параметри типу можуть застосовуватись усюди в оголошенні узагальнення, особливо де ви застосовуєте звичайні типи (щоправда існують деякі важливі обмеження; дивість секцію The Fine Print)
У вступі, ви побачили виклик (invocation) узагальненого типу при оголошенні List, як то List<Integer>. При виклику (так званого параметризованного типу) всі входження формального параметра типу (E в данному випадку) заміняються актуальним аргументом типу (в даному випадку Integer).
Ви можливо уявили, що List<Integer> створює версію List де E замінюється на Integer:
Дане уявлення корисне, проте дещо помилкове.
Воно користе, тому що параметризований тип List<Integer> справді має методи подібні до наведеного представлення.
Проте воно помилкове, тому що власне саме оголошення узагальнень ніколи не доповнюється таким чином. Не існує багатьох копій коду – ні в текстовому, ні в двійковому, ні на диску чи в пам’яті. Якщо ви С++ програміст, ви зрозумієте, що це значна відмінність від шаблонів C++.
Оголошення узагальненого типу компілюється один раз і поміщається у файл класу, як звичайний клас або оголошеня інтерфейсу.
Параметри типу є аналогами до звичайних параметрів, що використовуються в методах та конструкторах. Коли метод викликається актуальний аргумент підставляється у формальний параметр, і тіло методу виконується. Коли здійснюється оголошення узагальнення, актуальний аргумент типу підставляється у формальний параметр типу.
Зазначимо правила найменування. Ми рекомендуємо щоб ви використовували стислі назви (по можливості простий символ) для параметрів типу. Краще уникати нижнього регістру символі в таких назвах, що робить формальні параметри типу відмінними від звичайних класів та інтерфейсів. Багато контейнерних типів використовують E, для елементів, як в прикладах наведених вище. Ми розглянемо деякі інші умовності пізніше в прикладах.
В JDK 5.0 з’явилось декілька нових розширень до мови програмування Java. Одне з таких нововведень – це узагальнення (generics).
Ви можливо вже знайомі з подібними конструкціями з інших мов, найімовірніше з шаблонами (templates) в С++. Якщо так, то ви побачите, що є як подібності так і важливі відмінності. Якщо ви незнайомі з подібними конструкціями звідкілясь, це на краще, ви можете розпочати вивчення з нуля без помилкових уявлень.
Узагальнення дозволяють вам абстрагуватися над типами. Найбільш загальним прикладом є контейнерні типи, такі як у ієрархії Колекцій.
Ось типовий приклад даного виду:
List myIntList = new LinkedList(); // 1 myIntList.add(new Integer(0)); // 2 Integer x = (Integer) myIntList.iterator().next(); // 3
Приведення в рядку 3 є злегка надокучливим. Зазвичай, програміст знає, який тип даних поміщено у відповідний список. Проте, приведення є важливим. Компілятор може лише гарантувати, що ітератор поверне Object. Щоб забезпечити коректне присвоєння змінній типу Integer, необхідне приведення до типу.
Звичайно, приведення не тільки призводить до надмірності в коді. Це також призводить до можливостей помилок часу виконання, тож програма буде неправильною.
А що якщо програміст зможе вказати свої справжні наміри і задати список з конкретними обмеженнями щодо типів даних для розміщення у ньому? Це основна ідея узагальнень. Ось версія програмного фрагменту даного вище з використанням узагальнень:
List<Integer> myIntList = new LinkedList<Integer>(); // 1' myIntList.add(new Integer(0)); // 2' Integer x = myIntList.iterator().next(); // 3'
Запримітьте оголошення типу для змінної myIntList. Написавши List<Integer>, зазначаємо, що це не довільний список, а цілочисельний список. Ми говоримо, що List є узагальненим інтерфейсом, що отримує параметр типу – в даному випадку, Integer. Ми також зазначаємо параметр типу, коли створюємо об’єкт списку.
Зазначте, також, що приведення в рядку 3 відкинуто. Зараз, ви мабуть думаєте, що ми перетасували безладдя в коді . Замість приведення до Integer в рядку 3, ми маємо Integer як параметр типу в рядку 1. Проте, тут є значна велика відмінність. Тепер компілятор може провірити коректність типів в програмі під час компіляції. Коли ми кажемо, що myIntList є оголошеним з типом List<Integer>, це говорить нам дещо про змінну myIntList, яка міститеми правильні дані, коли б змінна не застосовувалась і компілятор гарантуватиме це. А приведення ж нам говорить інше, що програміст думає, що це вірно в даній точці коду.
В результаті ж, особливо в великих програмах, це покращує читабельність та робастність (правильну роботу) програми.
Визначення простого узагальнення
Ось невеликий витяг з визначень інтерфейсів List та Iterator в пакеті java.util:
public interface List <E> { void add(E x); Iterator<E> iterator(); } public interface Iterator<E> { E next(); boolean hasNext(); }
Усе це повинне бути зрозуміле вам, окрім вмісту в кутових дужках. Це – оголошення формального параметру типу інтерфейсів List and Iterator.
Параметри типу можуть застосовуватись усюди в оголошенні узагальнення, особливо де ви застосовуєте звичайні типи (щоправда існують деякі важливі обмеження; дивість секцію The Fine Print)
У вступі, ви побачили виклик (invocation) узагальненого типу при оголошенні List, як то List<Integer>. При виклику (так званого параметризованного типу) всі входження формального параметра типу (E в данному випадку) заміняються актуальним аргументом типу (в даному випадку Integer).
Ви можливо уявили, що List<Integer> створює версію List де E замінюється на Integer:
public interface IntegerList { void add(Integer x); Iterator<Integer> iterator(); }
Дане уявлення корисне, проте дещо помилкове.
Воно користе, тому що параметризований тип List<Integer> справді має методи подібні до наведеного представлення.
Проте воно помилкове, тому що власне саме оголошення узагальнень ніколи не доповнюється таким чином. Не існує багатьох копій коду – ні в текстовому, ні в двійковому, ні на диску чи в пам’яті. Якщо ви С++ програміст, ви зрозумієте, що це значна відмінність від шаблонів C++.
Оголошення узагальненого типу компілюється один раз і поміщається у файл класу, як звичайний клас або оголошеня інтерфейсу.
Параметри типу є аналогами до звичайних параметрів, що використовуються в методах та конструкторах. Коли метод викликається актуальний аргумент підставляється у формальний параметр, і тіло методу виконується. Коли здійснюється оголошення узагальнення, актуальний аргумент типу підставляється у формальний параметр типу.
Зазначимо правила найменування. Ми рекомендуємо щоб ви використовували стислі назви (по можливості простий символ) для параметрів типу. Краще уникати нижнього регістру символі в таких назвах, що робить формальні параметри типу відмінними від звичайних класів та інтерфейсів. Багато контейнерних типів використовують E, для елементів, як в прикладах наведених вище. Ми розглянемо деякі інші умовності пізніше в прикладах.
Generics - Java Tutorials (Bonus)
Частковий переклад: Volodimirg
Оригінал повністю: http://docs.oracle.com/javase/tutorial/extra/generics/index.html
Немає коментарів:
Дописати коментар