Концепции обобщений в Java

JavaJavaBeginner
Практиковаться сейчас

💡 Этот учебник переведен с английского с помощью ИИ. Чтобы просмотреть оригинал, вы можете перейти на английский оригинал

Введение

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


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL java(("Java")) -.-> java/ObjectOrientedandAdvancedConceptsGroup(["Object-Oriented and Advanced Concepts"]) java(("Java")) -.-> java/BasicSyntaxGroup(["Basic Syntax"]) java(("Java")) -.-> java/DataStructuresGroup(["Data Structures"]) java(("Java")) -.-> java/ProgrammingTechniquesGroup(["Programming Techniques"]) java/BasicSyntaxGroup -.-> java/output("Output") java/BasicSyntaxGroup -.-> java/type_casting("Type Casting") java/DataStructuresGroup -.-> java/arrays("Arrays") java/ProgrammingTechniquesGroup -.-> java/method_overloading("Method Overloading") java/ObjectOrientedandAdvancedConceptsGroup -.-> java/classes_objects("Classes/Objects") java/ObjectOrientedandAdvancedConceptsGroup -.-> java/arraylist("ArrayList") java/ObjectOrientedandAdvancedConceptsGroup -.-> java/generics("Generics") subgraph Lab Skills java/output -.-> lab-117688{{"Концепции обобщений в Java"}} java/type_casting -.-> lab-117688{{"Концепции обобщений в Java"}} java/arrays -.-> lab-117688{{"Концепции обобщений в Java"}} java/method_overloading -.-> lab-117688{{"Концепции обобщений в Java"}} java/classes_objects -.-> lab-117688{{"Концепции обобщений в Java"}} java/arraylist -.-> lab-117688{{"Концепции обобщений в Java"}} java/generics -.-> lab-117688{{"Концепции обобщений в Java"}} end

Создание обобщенного класса

В 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.