🔥 Чорна п’ятниця у FoxmindEd: знижки до 50% на ІТ курси онлайн! Поспішайте, пропозиція діє лише до 1.12!
Дізнатися більше
04.11.2024
5 хвилин читання

Обмеження generics та наслідування в Generic класах

Дженерики є потужним інструментом у Java, який дозволяє створювати універсальні класи, методи та інтерфейси, забезпечуючи типобезпеку та зменшуючи кількість помилок часу виконання. Однак, під час роботи з generics важливо враховувати певні обмеження та особливості їх використання.

Бажаєте освоїти професію Java Developer? Приєднуйтесь до програми “Від 0 до Strong Java Junior за 12 місяців”. Скористайтесь вигідною пропозицією від FoxmindEd!
Зареєструватись

Стирання типів

Generics були введені починаючи з Java 5, і постало питання, як забезпечити зворотну сумісність, щоб новий код з дженеріками не порушував роботу вже існуючих програм, написаних до їх появи. Було важливо, щоб старий код продовжував працювати так само, як і раніше, без необхідності змін.

Як ми вже згадували в першій частині статті, Raw Type — це дженерик-клас, де не зазначено конкретний тип параметра. Наприклад:

List list = new ArrayList();

У такому випадку цей список буде працювати з об’єктами типу Object. Це дозволяє використовувати той самий код без урахування параметризації типів, зберігаючи його функціональність.

Під час компіляції Java-код перетворюється на байт-код, який виконується віртуальною машиною. Якщо б байт-код зберігав інформацію про параметри типів, це могло б порушити сумісність із програмами, написаними до Java 5, де дженериків ще не існувало. Тому дженерики реалізували так, щоб вони не впливали на вже існуючий код, завдяки механізму, який називається “стиранням типів” (type erasure), що означає, що вся інформація про типи-параметри стирається. Наприклад, такі списки, як:

List<String> stringList = new ArrayList<>();

List<Bag> bagList = new ArrayList<>();

на рівні байт-коду перетворюються на звичайний:

List<Object>

Це означає, що в байт-коді не буде інформації про те, що перший список був для String, а другий — для Bag. Для JVM ці списки виглядатимуть однаково, як просто списки об’єктів (List<Object>).

Однак варто зазначити, що цей механізм також накладає певні обмеження на використання generics. Розглянемо їх детальніше.

Обмеження generics

Generics у Java надають багато переваг, таких як типобезпека, гнучкість і зручність, але також мають певні обмеження, з якими варто бути обізнаними під час їх використання. Ось основні обмеження generics у Java:

Оверлодинг методів із різними параметризованими типами

Generics не підтримують перевантаження методів (overloading) на основі різних параметрів типів. Якщо два методи мають однакову сигнатуру, але різні параметри типів, вони вважатимуться конфліктними через механізм стирання типів.

public class Test {

   public void print(List<String> list) {}

   public void print(List<Integer> list) {} // Це не скомпілюється через стирання типів, бо для JVM ці два методи приймають List<Object>

}

Не можна створювати екземпляри параметризованих типів

У Java generics використовуються лише для компіляції, а інформація про типи стирається на етапі виконання через механізм type erasure (стирання типів). Тому не можна створювати нові об’єкти параметризованих типів, оскільки вони більше не існують під час виконання.

public class Bag<T> {

   private T value;

   public Bag() {

        value = new T(); // Це не скомпілюється, оскільки T – це тип, який невідомий під час виконання

   }

}

Неможливо використовувати оператори instanceof з параметризованими типами

Оскільки під час виконання інформація про типи generics видаляється, ви не можете перевірити тип об’єкта через оператор instanceof для generics.

public class Bag<T> {

   public boolean isInstance(Object obj) {

       return obj instanceof T; // Це не скомпілюється

   }

}

Generics не можуть використовувати примітивні типи

Generics працюють лише з об’єктами, тому не можна використовувати примітивні типи, такі як int, char, boolean, тощо. Якщо вам потрібно використовувати примітивні типи, слід використовувати їх обгортки, наприклад, Integer для int, Double для double тощо. 

List<int> intList = new ArrayList<>(); // Це не скомпілюється

Примітивні типи в generics порушили б загальну концепцію, де всі параметризовані типи повинні бути сумісними з Object. Але примітивні типи не є об’єктами, вони не можуть бути перетворені на Object. 

Проте generics все ж таки можуть працювати із примітивами через механізм autoboxing/unboxing. Завдяки цьому generics можуть працювати з примітивними типами через їхні об’єктні обгортки

int num = 20;

List<Integer> numbers = new ArrayList<>();

numbers.add(num); // int автоматично перетвориться в Integer

int num2 = numbers.get(0); // Integer автоматично "розпаковується" в int

Generics не можуть бути статичними

Generics не можна використовувати для статичних змінних або методів у класі, оскільки статичні елементи не належать конкретному екземпляру класу і не можуть посилатися на параметри типу, які належать екземпляру.

public class Bag<T> {

   private static T value; // Це не скомпілюється

}

Неможливо використовувати generics у виключеннях

Generics не можна використовувати для оголошення або обробки винятків, оскільки це призвело б до потенційних проблем із сумісністю під час виконання.

public class MyException<T> extends Exception { // Це не скомпілюється

}
Підпишіться на наш Ютуб-канал! Корисні відео для програмістів чекають на вас! YouTube
Оберіть свій курс програмування! Шлях до кар’єри програміста починається тут! Подивитись

Висновок

Механізм дженериків у Java забезпечує потужну можливість працювати з параметризованими типами, що робить код більш типобезпечним і гнучким. Однак через необхідність підтримки зворотної сумісності зі старими версіями Java було впроваджено механізм стирання типів. Це рішення дозволяє використовувати generics без порушення роботи вже існуючих програм, але водночас накладає певні обмеження.

До основних обмежень дженериків належать неможливість створення екземплярів параметризованих типів, обмеження на використання дженериків у статичних контекстах, заборона перевантаження методів із різними параметризованими типами та неможливість використовувати generics із примітивними типами.

Академія програмування FoxmindEd випускає студентів, технічно готових вступити до лав програмістів мовою Java.

Розуміння цих обмежень та механізму стирання типів є важливим для ефективного використання generics у Java та допоможе уникнути типових помилок під час розробки. Хоча generics мають свої недоліки, правильне їхнє застосування дозволяє зробити код чистішим, безпечнішим і більш підтримуваним. 

FAQ
Чому було впроваджено стирання типів у generics?

Стирання типів у generics було запроваджено для забезпечення зворотної сумісності з попередніми версіями Java, де не було generics. Це рішення дозволяє старому коду працювати без змін.

Що таке "Raw Type" у контексті generics?

"Raw Type" — це дженерик-клас, де не вказано конкретний тип параметра, наприклад, List list = new ArrayList();. Такий список працює з об'єктами типу Object, дозволяючи зберігати функціональність без параметризації типів.

Чи можна перевантажувати методи з різними параметризованими типами?

Ні, generics не підтримують перевантаження методів із різними параметрами типів. Через стирання типів JVM вважає, що такі методи мають однакову сигнатуру, що спричиняє конфлікт.

Чому не можна створювати екземпляри параметризованих типів?

Параметризовані типи існують лише під час компіляції. Під час виконання інформація про них стирається, тому створення екземпляра параметризованого типу, як-от new T(), є неможливим.

Чи можна використовувати примітивні типи з generics?

Ні, generics працюють лише з об'єктами. Примітивні типи, такі як int чи char, не підтримуються, але можна використовувати їхні обгортки, наприклад, Integer для int.

Чому generics не можна використовувати в статичних змінних або методах?

Статичні змінні та методи належать класу, а не конкретному екземпляру, тому вони не можуть посилатися на параметри типу, визначені для екземпляра класу.

Розкажіть про свій досвід застосування generics у Java! Якщо є питання - ставте!

Додати коментар

Ваш імейл не буде опубліковано. Обов'язкові поля відзначені *

Зберегти моє ім'я, імейл та адресу сайту у цьому браузері для майбутніх коментарів