Введение
Обобщения в Java позволяют писать гибкий и повторно используемый код, предоставляя дополнительный уровень абстракции. Они позволяют создавать один алгоритм для нескольких типов объектов. Обобщения обеспечивают безопасность типов и избавляются от ошибок времени выполнения. В этом практическом занятии мы рассмотрим различные аспекты обобщений в Java.
Создание обобщенного класса
В Java мы можем создать обобщенный класс с помощью оператора ромба (<>). В следующем блоке кода мы создадим обобщенный класс под названием MyGenericClass, который может принимать любой тип объекта. Также мы создадим объект типа MyGenericClass с типами integer и string в качестве параметров.
// ~/project/MyGenericClass.java
class MyGenericClass<T> {
T field1;
public MyGenericClass(T field1) {
this.field1 = field1;
}
public T getField1() {
return field1;
}
}
public class Main {
public static void main(String[] args) {
MyGenericClass<Integer> myIntObj = new MyGenericClass<Integer>(100);
MyGenericClass<String> myStringObj = new MyGenericClass<String>("Hello World");
System.out.println(myIntObj.getField1());
System.out.println(myStringObj.getField1());
}
}
Для запуска кода откройте терминал и перейдите в каталог ~/project. Скомпилируйте код с помощью следующей команды:
javac MyGenericClass.java
После успешной компиляции запустите код с помощью следующей команды:
java Main
Результат выполнения кода будет:
100
Hello World
Создание обобщенного метода
В Java мы также можем создать обобщенный метод. Обобщенный метод может работать с разными типами объектов. В следующем блоке кода мы создадим обобщенный метод под названием printArray, который может выводить любой тип массива.
// ~/project/Main.java
public class Main {
public static <T> void printArray(T[] array) {
for (T element : array)
System.out.println(element);
}
public static void main(String[] args) {
Integer[] intArray = {1, 2, 3, 4, 5};
Double[] doubleArray = {1.1, 2.2, 3.3, 4.4};
String[] stringArray = {"Hello", "World"};
printArray(intArray);
printArray(doubleArray);
printArray(stringArray);
}
}
Для запуска кода откройте терминал и перейдите в каталог ~/project. Скомпилируйте код с помощью следующей команды:
javac Main.java
После успешной компиляции запустите код с помощью следующей команды:
java Main
Результат выполнения кода будет:
1
2
3
4
5
1.1
2.2
3.3
4.4
Hello
World
Создание ограниченного обобщенного метода
Мы также можем создать ограниченный обобщенный метод. Ограниченные обобщения ограничивают диапазон типов, которые может принимать метод. Мы используем ключевое слово extends, чтобы установить ограничение. В следующем блоке кода мы создадим ограниченный обобщенный метод под названием printNumbers, который может вывести числа объектов типа Number и его подклассов.
// ~/project/Main.java
public class Main {
public static <T extends Number> void printNumbers(T[] numbers) {
for (T number : numbers)
System.out.println(number);
}
public static void main(String[] args) {
Integer[] intNumbers = {1, 2, 3, 4, 5};
Double[] doubleNumbers = {1.1, 2.2, 3.3, 4.4};
printNumbers(intNumbers);
printNumbers(doubleNumbers);
}
}
Для запуска кода откройте терминал и перейдите в каталог ~/project. Скомпилируйте код с помощью следующей команды:
javac Main.java
После успешной компиляции запустите код с помощью следующей команды:
java Main
Результат выполнения кода будет:
1
2
3
4
5
1.1
2.2
3.3
4.4
Понимание безопасности типов в обобщениях
Обобщения обеспечивают безопасность типов, гарантируя, что мы определяем только один тип объектов для работы одновременно. В следующем блоке кода мы создадим ArrayList целых чисел без определения его параметра типа. Использовать обобщения без параметров типа - это плохая практика. Код скомпилируется, но при попытке обратиться к элементам другого типа будет возвращена ошибка времени выполнения.
// ~/project/Main.java
import java.util.ArrayList;
public class Main {
public static void main(String[] args) {
ArrayList integerList = new ArrayList();
integerList.add(1);
integerList.add(2);
integerList.add(3);
integerList.add("Hello World");
for (Object element : integerList) {
System.out.println((Integer)element);
}
}
}
Для запуска кода откройте терминал и перейдите в каталог ~/project. Скомпилируйте код с помощью следующей команды:
javac Main.java
После успешной компиляции запустите код с помощью следующей команды:
java Main
Результат выполнения кода будет:
1
2
3
Exception in thread "main" java.lang.ClassCastException: java.base/java.lang.String cannot be cast to java.base/java.lang.Integer
at Main.main(Main.java:13)
Мы получаем ошибку времени выполнения, потому что integerList содержит элементы типа Integer и String. Это происходит потому, что мы не определили параметр типа при создании integerList.
Понимание стирания типов в обобщениях
Java использует стирание типов в обобщениях, чтобы обеспечить отсутствие дополнительных накладных расходов во время выполнения. Компилятор заменяет параметр типа классом Object или родительским классом (в случае ограниченных обобщений) в процессе стирания типов. В следующем блоке кода мы увидим, как обобщения работают с примитивными типами на этапе компиляции и выполнения.
// ~/project/Main.java
import java.util.ArrayList;
public class Main {
public static void main(String[] args) {
ArrayList<Integer> integerList = new ArrayList<Integer>();
integerList.add(1);
integerList.add(2);
integerList.add(3);
int sum = 0;
for (int i = 0; i < integerList.size(); i++) {
sum += integerList.get(i);
}
System.out.println(sum);
}
}
Для запуска кода откройте терминал и перейдите в каталог ~/project. Скомпилируйте код с помощью следующей команды:
javac Main.java
После успешной компиляции запустите код с помощью следующей команды:
java Main
Результат выполнения кода будет:
6
В вышеприведенном блоке кода мы пытались добавить элементы integerList. Как мы знаем, примитивные типы, такие как int, не могут использоваться с обобщениями. В силу стирания типов параметр типа Integer заменяется классом Object на этапе компиляции. Однако автоупаковка и распаковка позволили нам выполнять сложение целых чисел на этапе компиляции. Во время выполнения код работал как обычное выполнение кода, без дополнительных накладных расходов.
Резюме
В этом практическом занятии мы изучили обобщения в Java. Мы рассмотрели основные концепции, связанные с обобщенными классами, обобщенными методами, ограниченными обобщенными методами, безопасностью типов в обобщениях и стиранием типов в обобщениях. Мы также увидели, как писать гибкий и повторно используемый код с использованием обобщений. Надеемся, что это практическое занятие помогла вам понять обобщения в Java.



