Что такое Generics в Java?

Обобщения в Java были представлены еще в 2004 году как новая функция языка программирования Java и были частью выпуска JDK 5. Он наиболее широко используется вместе со структурой коллекций Java. На сегодняшний день это одна из наиболее заметных и востребованных функций языка программирования Java.

Универсальная Java была найдена четырьмя лицами, а именно Гиладом Брачей, Мартином Одерским, Дэвидом Стотамиром и Филипом Уодлером в 1998 году. Это было расширение языка Java, которое поддерживало универсальные типы. Он должен был достичь двух основных целей:

  1. Тип безопасности
  2. Повторное использование кода

Определение обобщения в Java

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

Как дженерики реализованы в Java?

Обобщение осуществляется с помощью угловых скобок «». Скобки заключают в себе параметр типа «T». Пример, . Параметр типа «T» является заполнителем, который указывает, что тип данных будет назначен ему во время выполнения. Например, универсальный класс будет определен как:

public class MyGenericClass (…)

Ниже приведены стандартные параметры типа:

  • T: Тип
  • E: элемент
  • N: номер
  • К: Ключ
  • V: значение

S, U, V и т. Д. Используются для определения второго, третьего и четвертого параметров соответственно в случае использования нескольких параметров.

Понимание обобщений в Java

К настоящему времени вам может быть интересно, что такое безопасность типов и как она работает? Или как универсальные классы, интерфейсы, конструкторы и методы отличаются от наших обычных классов и методов, которые делают их повторно используемыми? Давай выясним.

Java как статически типизированный язык требует, чтобы вы объявили «тип», который является типом данных значения, содержащегося в переменной, перед ее использованием.

Пример: String myString =”eduCBA”;

Здесь «String» - это тип данных, «myString» - это переменная, которая будет содержать значение типа String.

Теперь, если вы попытаетесь передать логическое значение вместо строки, например:

String myBooleanStr = true;

Вы сразу получите сообщение об ошибке во время компиляции, в котором говорится: «Несоответствие типов: невозможно преобразовать логическое значение в строковое».

Как мы можем добиться повторного использования кода с помощью Generics?

Теперь давайте определим обычный метод:

public static void welcome(String name)(
System.out.println("welcome to " + name);
)

Этот метод может быть вызван только путем передачи строкового параметра. Например:

welcome(“eduCBA”);

Его вывод будет «добро пожаловать в eduCBA».

Однако вы не можете вызвать этот метод, минуя любые другие типы данных, такие как целочисленные или логические. Если вы попытаетесь это сделать, вам будет выдано сообщение об ошибке, сообщающей: «Метод welcome (String) в типе Runner не применим для аргументов (логическое значение)». Это означает, что вы не можете передать любой другой тип данных методу, который принимает только строку в качестве параметра.

Это также означает, что если вы хотите вызвать подобный метод для другого типа данных, вам придется написать новый метод, который принимает требуемый тип данных в качестве параметра. Эта особенность переписывания методов с параметрами различных типов данных также известна как перегрузка методов. Основным недостатком этого является увеличение размера вашего кода.

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

Определение общего метода:

public static void welcome(T t)(
System.out.println("it is " + t);
)

Примечание . Здесь «t» - это объект типа T. T будет присвоен тип данных, который используется для вызова метода.

Теперь вы можете повторно использовать этот метод, вызывая его для строки, когда это необходимо, или логического, или целого, или любого другого типа данных.

welcome("educate");
Integer Myint = 1;
welcome(Myint)
welcome(true);

Вышеуказанные утверждения обеспечат следующий вывод:

Это Educa
Это 1
Это правда

Таким образом, используя дженерики, мы можем повторно использовать наш метод для разных типов данных.

Как мы достигаем безопасности типов с помощью обобщений

Одно из основных различий между массивами и коллекциями состоит в том, что массивы могут хранить только однородные данные, тогда как коллекции могут хранить гетерогенные данные. То есть Коллекции могут хранить любой пользовательский тип данных / объекты.

ПРИМЕЧАНИЕ. Коллекции могут содержать только объекты (пользовательский тип данных), но не примитивный тип данных. Для работы с примитивными данными в коллекциях типов используются классы-обертки.

Теперь давайте рассмотрим ArrayList.

ArrayList myList = new ArrayList();

Давайте добавим данные типа String, Integer и Double в объект ArrayList.

myList.add("eduCBA");
myList.add(1);
myList.add(5.2);

При печати объекта ArrayList мы видим, что он содержит следующие значения: (eduCBA, 1, 5.2).

Теперь, если вы хотите извлечь эти значения в переменные, вам нужно будет их типизировать.

String someStr = (String)myList.get(0);
Integer someInt = (Integer)myList.get(1);
Double someFlt = (Double)myList.get(2);

Если вы не ввели тип, вам будет выдана ошибка во время компиляции с указанием «Несоответствие типов: невозможно преобразовать из объекта в строку».

Из этого вы можете сделать вывод, что при получении объектов из вашего ArrayList вам необходимо типизировать его к соответствующим типам. Возникает вопрос: как вы узнаете, к какому типу данных это относится? В реальном времени ваш ArrayList будет содержать тысячи записей, и его типизация с различными типами данных для каждого отдельного объекта не будет возможной. Вы можете в конечном итоге привести его к неправильному типу данных. Что происходит потом?

На этот раз вы не получите ошибку времени компиляции, но выдадите ошибку времени выполнения, в которой будет указано «Исключение в потоке« main »java.lang.ClassCastException: java.lang.Integer не может быть приведен к java.lang.String в com.serviceClasess.Runner». .main (Runner.java:43)».

Поскольку мы не можем гарантировать тип данных, представленных внутри коллекции (в данном случае ArrayList), они считаются небезопасными для использования в отношении типа. Здесь в игру вступают дженерики для обеспечения безопасности типов.

Использование ArrayList с Generics:

ArrayList myList = new ArrayList();

Обратите внимание, что внутри угловых скобок «» указан тип String, что означает, что данная конкретная реализация ArrayList может содержать только данные типа String. Если вы попытаетесь добавить к нему любой другой тип данных, он просто выдаст ошибку времени компиляции. Здесь вы сделали свой тип ArrayList безопасным, исключив возможность добавления другого типа данных, отличного от «String».

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

String someStr = myList.get(0);

Как Generics в Java облегчает работу?

Это помогает сделать ваши коллекции безопасными по типу, таким образом гарантируя, что ваш код не потерпит неудачу на более позднем этапе из-за исключительной ситуации во время выполнения. Это также избавляет кодировщика от необходимости настраивать каждый объект в коллекции, делая разработку кода быстрее и проще. Используя универсальные классы и методы, можно также повторно использовать код в соответствии с требуемым типом данных во время реализации.

Что еще вы можете сделать с Generics в Java?

До сих пор мы видели, как мы можем достичь безопасности типов и повторного использования кода с помощью обобщений. Теперь давайте посмотрим на другие функции, которые предоставляют дженерики. Они есть:

  1. Ограниченные и кратно ограниченные типы
  2. Введите подстановочные знаки

Ограниченный тип: в случае ограниченного типа тип данных параметра ограничен определенным диапазоном. Это достигается с помощью ключевого слова «extends».

Например, давайте рассмотрим универсальный класс с параметром ограниченного типа, который расширяет интерфейс Runnable:

class myGenericClass()

Теперь при создании своего объекта в другом классе:

myGenericClass myGen = new myGenericClass();

Вышеупомянутое утверждение выполнится идеально без каких-либо ошибок. То есть в случае ограниченного типа вы можете передать тот же тип класса или его дочерний тип класса. Кроме того, вы можете привязать тип параметра к интерфейсу и передавать его реализации при вызове, как в нашем примере выше.

Что произойдет, если вы попытаетесь использовать любой другой тип параметра?

myGenericClass myGen = new myGenericClass();

В приведенном выше случае вы получите ошибку во время компиляции, в которой будет указано «Несоответствие границ: тип Integer не является допустимой заменой для типа трансляции типа myGenericClass».

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

Class myGeneric()

В этом случае вы можете передать любой тип, который расширяет класс Number и реализует интерфейс Runnable. Однако при использовании нескольких ограниченных типов следует отметить несколько вещей:

  1. Мы не можем продлевать более одного класса одновременно.
  2. Мы можем расширять любое количество интерфейсов за раз, то есть нет никаких ограничений для интерфейсов.
  3. Имя класса всегда должно идти первым, за которым следует имя интерфейса, если нет, это приведет к ошибке во время компиляции.

Подстановочные знаки типа: они представлены символом «?» - вопросительного знака. Он использует два основных ключевых слова:

extends (для определения верхней границы) и super (для определения нижней границы).

Например,

ArrayList al

Этот объект ArrayList «al» будет содержать любые данные типа T и все его подклассы.

ArrayList al

Этот объект ArrayList «al» будет содержать любые данные типа T и все его суперклассы.

Преимущества дженериков в Java

1. Гибкость . Generics обеспечивает гибкость нашего кода для размещения различных типов данных с помощью универсальных классов и методов.

2. Ведение и повторное использование кода . Благодаря универсальным классам и методам нет необходимости переписывать код в случае изменения требований на более позднем этапе, что облегчает поддержку и повторное использование кода.

3. Безопасность типов: обеспечивает безопасность типов в структуре сбора, определяя тип данных, который коллекция может хранить заранее, и исключая любые шансы сбоя во время выполнения из-за ClassCastException.

4. Устранение необходимости типизации: поскольку типы данных, которые содержатся в коллекциях, уже определены, нет необходимости в типизации во время поиска. Это уменьшает длину кода, а также уменьшает усилие кодера.

Обобщения в навыках Java

Чтобы работать с Generics, вы должны хорошо разбираться в основах Java. Вы должны понимать, как работает проверка типов и приведение типов. Необходимы глубокие знания других понятий, таких как перегрузка методов, отношения между родительскими и дочерними классами, интерфейсы и их реализации. Также важно понимать разницу между примитивными типами данных (системный тип данных) и объектами (пользовательский тип данных), когда речь идет о работе с платформой сбора.

Почему мы должны использовать Generics в Java?

Использование обобщений делает наш код более понятным, поскольку уменьшает необходимость перезаписи кода, специфичного для типа данных, каждый раз, когда происходит изменение в требовании. Используя обобщенный ограниченный тип, вы можете ограничить тип данных и в то же время обеспечить гибкость своего кода, определив его диапазон. Ваш код с меньшей вероятностью даст сбой на более позднем этапе, поскольку он обеспечивает безопасность типов, делая ваш код менее подверженным ошибкам.

Область применения дженериков в Java

Обобщенная область ограничена временем компиляции. Это означает, что универсальная концепция применима только во время компиляции, но не во время выполнения. Например,

ArrayList myList = new ArrayList();

ArrayList myList = new ArrayList();

ArrayList myList = new ArrayList();

ArrayList myList = new ArrayList();

Здесь все вышеперечисленные четыре утверждения - одно и то же. Они позволят добавлять данные любого типа в объект списка.

Вывод

Обобщения делают кодирование легким для программиста. Это уменьшает шансы встретить ClassCastException во время выполнения, обеспечивая строгую проверку типов. Полностью исключает необходимость приведения типов, что означает, что нужно писать меньше кода. Это дает нам возможность разрабатывать общие алгоритмы, которые не зависят от типа данных, с которыми они работают.

Рекомендуемые статьи

Это было руководство к тому, что такое Generics в Java ?. Здесь мы обсудили Навыки, Область применения, работу, Понимание и Преимущества Обобщения в Java. Вы также можете просмотреть наши другие предлагаемые статьи, чтобы узнать больше -

  1. Что такое общий интерфейс шлюза
  2. Как установить Java 8
  3. что такое мыло
  4. Что такое JavaScript?
  5. Java Booleans