Menu
Coddy logo textTech

Введение в обобщения

Часть раздела Объектно-ориентированное программирование путешествия по Java на Coddy. Урок 53 из 87.

Представьте, что вам нужно создать класс, который хранит значение и извлекает его позже. Без generics вам пришлось бы писать отдельные классы для каждого типа: один для Integer, один для String, один для Double и так далее. Это приводит к дублированию кода и сложностям с сопровождением.

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

// Без дженериков — использует Object, требует приведения типов
List list = new ArrayList();
list.add("Hello");
String s = (String) list.get(0);  // Требуется ручное приведение типов

// С дженериками — типобезопасно, без приведения типов
List<String> list = new ArrayList<String>();
list.add("Hello");
String s = list.get(0);  // Приведение типов не требуется

Угловые скобки <T> обозначают параметр типа. Когда вы пишете List<String>, вы сообщаете компилятору, что этот список будет содержать только строки. Если вы попытаетесь добавить целое число, компилятор немедленно обнаружит ошибку, вместо того чтобы ошибка возникла во время выполнения.

Generics дают два ключевых преимущества: они устраняют необходимость в явном casting и обнаруживают ошибки типов на этапе compile, а не во время выполнения. Благодаря этому ваш код становится одновременно безопаснее и чище. В следующих уроках мы рассмотрим, как создавать собственные обобщённые class и method.

challenge icon

Задание

Легко

Давайте создадим простую систему хранения, которая продемонстрирует возможности обобщений, сравнив старый способ (с использованием Object и приведения типов) с новым способом (с использованием обобщений и проверкой типов).

Вы создадите три файла, чтобы увидеть разницу на практике:

  • OldBox.java: Create класс, который хранит значения способом, использовавшимся до обобщений. Ваш OldBox должен иметь private поле content типа Object. Добавьте конструктор, который accepts Object и сохраняет его, а также метод getContent(), который возвращает Object. Этот подход работает, но requires приведения типов при получении значений.
  • GenericBox.java: Теперь Create современную версию с проверкой типов, используя обобщения. Ваш GenericBox должен использовать параметр типа T в своём объявлении. Ему нужно private поле content типа T, конструктор, который accepts значение типа T, и метод getContent(), который возвращает тип T. При использовании этого класса приведение типов не needed!
  • Main.java: Продемонстрируйте оба подхода рядом. Вы получите два входных значения: слово (String) и число (целое число).

    Сначала используйте старый подход: Create OldBox, хранящий слово, затем получите его с приведением к String и выведите: OldBox (with cast): [value]

    Затем используйте подход с обобщениями: Create GenericBox<String>, хранящий то же слово, получите его без приведения типов и выведите: GenericBox (no cast): [value]

    Затем покажите работу обобщений с целыми числами: Create GenericBox<Integer>, хранящий число, получите его и выведите: GenericBox Integer: [value]

    Наконец, выведите пустую строку, а затем: Type safety: Generics catch errors at compile time!

Вы получите два входных значения по порядку: слово (String) и число (целое число в виде String, которое вы распарсите).

Обратите внимание на ключевое различие: с OldBox вы должны выполнить приведение (String) box.getContent() и надеяться, что получили правильный тип. С GenericBox компилятор знает тип, и приведение не требуется. В этом заключается основное преимущество обобщений: проверка типов без неудобств, связанных с приведением!

Попробуйте сами

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String word = scanner.nextLine();
        int number = Integer.parseInt(scanner.nextLine());
        
        // TODO: Создайте OldBox, хранящий слово
        // Retrieve with cast to String and print: "OldBox (with cast): [value]"
        
        // TODO: Создайте GenericBox<String>, хранящий слово
        // Retrieve without casting and print: "GenericBox (no cast): [value]"
        
        // TODO: Create a GenericBox<Integer> storing the number
        // Retrieve and print: "GenericBox Integer: [value]"
        
        // TODO: Выведите пустую строку, затем выведите:
        // "Type safety: Generics catch errors at compile time!"
    }
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование

Потренируйтесь самостоятельно: Онлайн-компилятор Java