Обобщенные методы
Часть раздела Объектно-ориентированное программирование путешествия по Java на Coddy — урок 55 из 87.
В то время как обобщенные классы позволяют целым классам работать с различными типами, иногда требуется, чтобы только один метод был универсальным. Обобщенный метод объявляет свой собственный параметр типа независимо от любых обобщений на уровне класса.
Параметр типа указывается перед типом возвращаемого значения в сигнатуре метода:
public class Utility {
public static <T> void printArray(T[] array) {
for (T element : array) {
System.out.println(element);
}
}
}
String[] names = {"Alice", "Bob"};
Integer[] numbers = {1, 2, 3};
Utility.printArray(names); // Работает с String[]
Utility.printArray(numbers); // Работает с Integer[]Обратите внимание на <T>, расположенный между модификаторами и типом возвращаемого значения. Это объявляет T как параметр типа для этого конкретного метода. Компилятор выводит фактический тип из передаваемых вами аргументов.
Обобщенные методы также могут возвращать обобщенный тип:
public static <T> T getFirst(T[] array) {
if (array.length > 0) {
return array[0];
}
return null;
}
String first = getFirst(new String[]{"a", "b"}); // Возвращает "a"Вы можете использовать несколько параметров типа, когда это необходимо:
public static <K, V> void printPair(K key, V value) {
System.out.println(key + ": " + value);
}
printPair("Age", 25); // String и Integer
printPair(1, "First"); // Integer и StringОбобщенные методы особенно полезны в утилитарных классах, где вам нужны гибкие, переиспользуемые операции без создания обобщенного класса.
Задание
ЛегкоДавайте создадим набор утилит, который продемонстрирует гибкость универсальных (generic) методов. Вы создадите класс с переиспользуемыми методами, которые работают с любым типом данных, показывая, как универсальные методы могут существовать независимо от универсальных классов.
Вы организуете свой код в двух файлах:
ArrayUtils.java: Создайте вспомогательный класс, содержащий универсальные методы для работы с массивами. Ваш класс должен иметь три статических универсальных метода:getLast— универсальный метод, который принимает массив типаTи возвращает последний элемент. Если массив пуст, вернитеnull.swap— универсальный метод, который принимает массив типаTи два целочисленных индекса, а затем меняет местами элементы в этих позициях. Этот метод ничего не возвращает.printWithLabel— универсальный метод с двумя параметрами типаKиV. Он принимает метку типаKи значение типаV, а затем выводит их в формате:[label]: [value]Main.java: Примените ваши универсальные методы на практике! Вы получите четыре входных значения: два слова (Strings) и два числа (integers).Сначала создайте массив
String[], содержащий оба слова. ИспользуйтеgetLast, чтобы получить последний элемент, и выведите:Last word: [result]Затем создайте массив
Integer[], содержащий оба числа. Вызовитеswap, чтобы поменять местами элементы с индексами 0 и 1, а затем выведите:After swap: [first], [second]Наконец, продемонстрируйте работу универсального метода с двумя параметрами, вызвав
printWithLabelдважды:- Один раз с первым словом в качестве метки и первым числом в качестве значения
- Один раз с первым числом в качестве метки и вторым словом в качестве значения
Вы получите четыре входных значения по порядку: первое слово, второе слово, первое число, второе число.
Обратите внимание, как каждый метод объявляет свои собственные параметры типа с помощью <T> или <K, V> перед типом возвращаемого значения. Компилятор выводит фактические типы из аргументов, которые вы передаете — нет необходимости указывать их явно при вызове методов!
Шпаргалка
Обобщенный метод объявляет свой собственный параметр типа, независимый от любых обобщений на уровне класса. Параметр типа указывается перед типом возвращаемого значения в сигнатуре метода.
Базовый синтаксис обобщенного метода:
public static <T> void printArray(T[] array) {
for (T element : array) {
System.out.println(element);
}
}<T> помещается между модификаторами и типом возвращаемого значения. Компилятор выводит фактический тип на основе переданных аргументов.
Обобщенные методы могут возвращать обобщенный тип:
public static <T> T getFirst(T[] array) {
if (array.length > 0) {
return array[0];
}
return null;
}Можно использовать несколько параметров типа:
public static <K, V> void printPair(K key, V value) {
System.out.println(key + ": " + value);
}
printPair("Age", 25); // String и Integer
printPair(1, "First"); // Integer и StringОбобщенные методы полезны в утилитарных классах для создания гибких, переиспользуемых операций без необходимости создания обобщенного класса.
Попробуйте сами
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// Чтение входных данных
String firstWord = scanner.nextLine();
String secondWord = scanner.nextLine();
int firstNumber = scanner.nextInt();
int secondNumber = scanner.nextInt();
// TODO: Создайте массив String[], содержащий оба слова
// TODO: Используйте ArrayUtils.getLast, чтобы получить последний элемент
// Выведите: "Last word: [result]"
// TODO: Создайте массив Integer[], содержащий оба числа
// TODO: Вызовите ArrayUtils.swap, чтобы поменять местами элементы с индексами 0 и 1
// Выведите: "After swap: [first], [second]"
// TODO: Вызовите ArrayUtils.printWithLabel, используя firstWord в качестве метки и firstNumber в качестве значения
// TODO: Вызовите ArrayUtils.printWithLabel, используя firstNumber в качестве метки и secondWord в качестве значения
}
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП
Внешние файлыВведение в ООПКлассы и объектыКлючевое слово thisМетодыПоля (атрибуты)Метод-конструкторПерегрузка конструкторовИтоги — Простой калькулятор4Наследование
Основы наследования (extends)Ключевое слово superПереопределение методов (@Override)Цепочка конструкторовКласс ObjectОдиночное и многоуровневое наследованиеПочему нет множественного наследования классовИтоги — Иерархия сотрудников7Специальные методы и класс Object
Метод toString()equals() и hashCode()Метод clone()compareTo() и ComparableИнтерфейс ComparatorПовторение — Кастомная сортировка2Модификаторы доступа и инкапсуляция
Обзор уровней доступаМетоды геттеры и сеттерыСокрытие данныхКлючевое слово finalИтоги — Менеджер банковского счета5Полиморфизм
Основы перегрузки методовПереопределение методов (Run-Time)Upcasting и DowncastingОператор instanceofАбстрактные классы и методыИтоги: Калькулятор фигур8Продвинутые концепции ООП
Композиция против наследованияАгрегация против композицииВнутренние, вложенные и анонимные классыEnums и методы перечисленийRecords (Java 16+)Sealed-классы (Java 17+)11Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн SingletonПаттерн FactoryПаттерн BuilderПаттерн ObserverПаттерн Strategy3Свойства класса и статические члены
Переменные экземпляра и статические переменныеСтатические методыСтатические блокиКонстанты (static final)Итоги: Счетчик и утилиты6Интерфейсы и абстрактные классы
Введение в интерфейсыРеализация интерфейсовРеализация нескольких интерфейсовDefault и Static методы в интерфейсахАбстрактные классы vs ИнтерфейсыФункциональные интерфейсыИтоги — Платежная система9Обобщения
Введение в обобщенияОбобщенные классыОбобщенные методыОграниченные параметры типаСимволы подстановки (Wildcards: ?, extends, super)Итоги — Обобщенный контейнер12Паттерны проектирования. Часть 2
Паттерн «Команда»Паттерн «Адаптер»Паттерн «Декоратор»Паттерн «Шаблонный метод»Паттерн «Состояние»Паттерн «Компоновщик»Паттерн «Итератор»