Skip to content

AndreiPanchenko/JavaLabWork4

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

8 Commits
 
 
 
 

Repository files navigation

Отчёт по лабораторной работе №4: Обобщённое программирование в Java

Содержание

  1. Формулировка задач
  2. Структура проекта
  3. Реализация и комментарии
  4. Тестирование
  5. Заключение

Формулировка задач

Задание 1.2 - Универсальное хранилище

Создайте сущность Хранилище, которая обладает следующими характеристиками:

  • Может хранить один произвольный объект в один момент времени.
  • Хранилище неизменяемо.
  • Объект кладется в Хранилище при его создании. В качестве объекта может быть сохранено также и значение null.
  • Хранилище может вернуть ссылку на Объект.
  • Если вместо объекта хранится null, необходимо вернуть какое-либо альтернативное значение.
  • Метод получения значения должен работать с тем типом данных, который был указан во время создания объекта

Выполните следующие задания:

  • Создайте Хранилище чисел, положите туда значение null. Передайте Хранилище в какой-либо метод, извлеките значение, и выведите его на экран. Альтернативой должно быть число 0.
  • Создайте Хранилище чисел, положите туда значение 99. Передайте Хранилище в какой-либо метод, извлеките значение, и выведите его на экран. Альтернативой должно быть число -1.
  • Создайте Хранилище строк, положите туда значение null. Передайте Хранилище в какой-либо метод, извлеките значение, и выведите его на экран. Альтернативой должна быть строка "default".
  • Создайте Хранилище строк, положите туда значение "hello". Передайте Хранилище в какой-либо метод, извлеките значение, и выведите его на экран. Альтернативой должна быть строка "hello world".

Задание 1.5 - Универсальная линия

Универсальная линия Измените сущность Линия из задачи 2.6.3, таким образом, чтобы

  • При создании её объекта можно было точно указать тип точки, на которой расположена линия: двухмерная или трехмерная (из задачи 2.3.5).
  • Методы получения и установки значения Точки также могли работать с типом указанным при создании объекта.
  • Граница стирания не может быть хуже двумерной точки. Создайте и выведите на экран произвольную линию в трехмерном пространстве

Задание 2.1 - Сдвиг линии

Сдвинуть линию. Создайте метод, принимающий Линию из задачи 3.1.5 (с любой допустимой параметризацией) сдвигающей точку начала на 10 единиц по оси X. Например, если X был 5, то должен стать 15, если X был -7, то должен стать -17.

Задание 3.1 - Map (Функция)

Функция. Разработайте такой метод, который будет принимать список значений типа T, и объект имеющий единственный метод apply. Данный метод надо применить к каждому элементу списка, и вернуть новый список значений типа P, при этом типы T и P могут совпадать, а могут не совпадать. Используйте разработанный метод следующим образом:

  1. Передайте в метод список со значениями:"qwerty", "asdfg", "zx", а получите список чисел, где каждое число соответствует длине каждой строки.
  2. Передайте в метод список со значениями: 1,-3,7, а получите список в котором все отрицательные числа стали положительными, а положительные остались без изменений
  3. Передайте в метод список состоящий из массивов целых чисел, а получите список в котором будут только максимальные значения каждого из исходных массивов

Задание 3.2 - Filter (Фильтр)

Фильтр. Разработайте такой метод, который будет принимать список значений типа T и объект имеющий единственный метод test (принимает T и возвращает boolean). Верните новый список типа T, из которого удалены все значения не прошедшие проверку условием. Используйте разработанный метод следующим образом:

  1. Передайте в метод список со значениями: "qwerty", "asdfg", "zx", и отфильтруйте все строки имеющие менее трех символов
  2. Передайте в метод список со значениями: 1,-3,7, и отфильтруйте все положительные элементы
  3. Передайте в метод список состоящий из массивов целых чисел, а получите список в котором будут только те массивы, в которых нет ни одного положительного элемента

Задание 3.3 - Reduce (Сокращение)

Сокращение. Разработайте такой метод, который будет принимать список значений типа T и способ с помощью которого список значений можно свести к одному значению типа T, которое и возвращается из метода. Используйте разработанный метод следующим образом:

  1. Передайте в метод список со значениями: "qwerty", "asdfg", "zx", и сформируйте одну большую строку, которая состоит из всех строк исходного списка.
  2. Передайте в метод список со значениями: 1,-3,7, и верните сумму всех значений исходного списка.
  3. Имеется список, состоящий из списков целых чисел, получите общеe количество элементов во всех списках. Подсказка: решить задачу можно в одно действие или последовательно использовать методы из 3.3.1 и 3.3.3. Далее необходимо изменить разработанный метод таким образом, чтобы данный метод гарантированно не возвращал null и не выбрасывал ошибок в том случае, если исходный список пуст

Задание 3.4 - Collect (Коллекционирование)

Коллекционирование. Разработайте такой метод, который будет возвращать коллекцию типа P со значениями типа T. Данный метод будет принимать:

  1. Список исходных значений
  2. Способ создания результирующей коллекции
  3. Способ передачи значений исходного списка в результирующую коллекцию. Используйте разработанный метод следующим образом:
  4. Передайте в метод список со значениями: 1,-3,7, и верните их разбитыми на два подсписка, в одном из которых будут только положительные числа, а в другом только отрицательные.
  5. Передайте в метод список со значениями: "qwerty", "asdfg", "zx", "qw" и верните их разбитыми на подсписки таким образом, чтобы в любом подсписке были строки только одинаковой длины
  6. Передайте в метод список со значениями: "qwerty", "asdfg", "qwerty", "qw" и верните набор такого вида, который не может содержать одинаковые объекты.

Структура проекта

lab4_java/
├── src/
│   └── ru/
│       └── panchenko/
│           ├── main/
│           │   ├── Main.java                    # Главный класс с меню и демонстрацией
│           │   └── InputChecker.java            # Класс для безопасного ввода данных
│           ├── collections/
│           │   └── Storage.java                 # Универсальное хранилище (Задание 1.2)
│           ├── geometry/
│           │   ├── Point.java                   # Класс 2D точки
│           │   ├── Point3D.java                 # Класс 3D точки (наследник Point)
│           │   ├── Line.java                    # Обычный класс линии
│           │   ├── LineGeneric.java             # Универсальная линия (Задание 1.5)
│           │   └── LineUtils.java               # Утилиты для работы с линиями (Задание 2.1)
│           └── functional/
│               ├── Function.java                # Функциональный интерфейс для Map
│               ├── Predicate.java               # Функциональный интерфейс для Filter
│               ├── BinaryOperator.java          # Функциональный интерфейс для Reduce
│               └── GenericMethods.java          # Универсальные методы (Задания 3.1-3.4)
└── README.md

Реализация и комментарии

Задание 1.2 - Универсальное хранилище

Ключевой код:

public class Storage<T> {
    private final T value;

    public Storage(T value) {
        this.value = value;
    }

    public T getValue() {
        return value;
    }

    public T getValueOrDefault(T defaultValue) {
        return value != null ? value : defaultValue;
    }
}

Комментарии к реализации:

  • Параметризованный тип T: Позволяет хранить объекты любого типа, обеспечивая безопасность типов на этапе компиляции
  • final поле value: Гарантирует неизменяемость хранилища после создания - объект становится иммутабельным
  • Конструктор с параметром: Обеспечивает обязательную инициализацию значения при создании экземпляра
  • Метод getValueOrDefault: Предоставляет защиту от null значений, возвращая альтернативное значение по умолчанию

Статические методы для демонстрации:

public static void printNumberStorage(Storage<Integer> storage) {
    Integer value = storage.getValueOrDefault(0);
    System.out.println("Значение: " + value);
}

public static void printStringStorage(Storage<String> storage) {
    String value = storage.getValueOrDefault("default");
    System.out.println("Значение: " + value);
}

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

Задание 1.5 - Универсальная линия

Ключевой код:

public class LineGeneric<T extends Point> {
    private T start;
    private T end;

    public LineGeneric(T start, T end) {
        if (start == null || end == null) {
            throw new IllegalArgumentException("Точки начала и конца не могут быть null");
        }
        if (start.equals(end)) {
            throw new IllegalArgumentException("Точки начала и конца не могут совпадать");
        }
        this.start = start;
        this.end = end;
    }

    public double getLength() {
        double dx = end.getX() - start.getX();
        double dy = end.getY() - start.getY();

        if (start instanceof Point3D && end instanceof Point3D) {
            Point3D start3D = (Point3D) start;
            Point3D end3D = (Point3D) end;
            double dz = end3D.getZ() - start3D.getZ();
            return Math.sqrt(dx * dx + dy * dy + dz * dz);
        }
        return Math.sqrt(dx * dx + dy * dy);
    }
}

Комментарии к реализации:

  • Ограниченный параметризованный тип T extends Point: Гарантирует, что тип T будет наследником Point, обеспечивая доступ к базовым методам getX() и getY() для всех типов точек
  • Строгие проверки в конструкторе: Предотвращают создание некорректных линий - с null точками или совпадающими точками начала и конца
  • Полиморфный метод getLength(): Автоматически определяет размерность точек во время выполнения и вычисляет длину соответственно (2D или 3D формула)
  • Инкапсуляция данных: Геттеры возвращают копии точек для защиты от несанкционированного внешнего изменения внутреннего состояния

Задание 2.1 - Сдвиг линии

Ключевой код:

public class LineUtils {
    public static <T extends Point> void shiftLineStartX(LineGeneric<T> line) {
        T start = line.getStart();
        start.setX(start.getX() + 10);
    }

    public static void shiftLineStartX(Line line) {
        Point start = line.getStart();
        Point newStart = new Point(start.getX() + 10, start.getY());
        line.setStart(newStart);
    }
}

Комментарии к реализации:

  • Универсальный метод для LineGeneric: Работает с любой параметризацией линии, где тип точки является наследником Point
  • Перегрузка метода для обычной линии: Обеспечивает совместимость с нефункциональным классом Line, демонстрируя обратную совместимость
  • Разные стратегии изменения: Для LineGeneric используется прямой вызов сеттера существующей точки, для обычной Line создаётся новая точка с изменёнными координатами
  • Ограничение типа параметра: T extends Point гарантирует наличие метода setX() у всех используемых типов точек

Задание 3.1 - Map (Функция)

Ключевой код:

public static <T, R> List<R> map(List<T> list, Function<T, R> function) {
    List<R> result = new ArrayList<>();
    for (T item : list) {
        result.add(function.apply(item));
    }
    return result;
}

Комментарии к реализации:

  • Два параметра типа: T определяет тип входных элементов, R - тип выходных элементов, что позволяет преобразовывать данные между разными типами
  • Функциональный интерфейс Function: Позволяет передавать лямбда-выражения или ссылки на методы для определения логики преобразования
  • Сохранение порядка элементов: Элементы обрабатываются и добавляются в результирующий список в том же порядке, что и в исходном списке
  • Иммутабельность исходных данных: Исходный список остаётся полностью неизменным, создаётся новый список с результатами

Примеры использования:

// Преобразование строк в их длины
List<String> strings = Arrays.asList("qwerty", "asdfg", "zx");
List<Integer> lengths = GenericMethods.map(strings, s -> s.length());

// Преобразование чисел в абсолютные значения  
List<Integer> numbers = Arrays.asList(1, -3, 7);
List<Integer> absoluteValues = GenericMethods.map(numbers, n -> Math.abs(n));

Задание 3.2 - Filter (Фильтр)

Ключевой код:

public static <T> List<T> filter(List<T> list, Predicate<T> predicate) {
    List<T> result = new ArrayList<>();
    for (T item : list) {
        if (predicate.test(item)) {
            result.add(item);
        }
    }
    return result;
}

Комментарии к реализации:

  • Функциональный интерфейс Predicate: Определяет условие фильтрации через единственный метод test(), возвращающий boolean
  • Условное добавление элементов: Каждый элемент проверяется предикатом и добавляется в результат только при выполнении условия
  • Отсутствие побочных эффектов: Исходный список остаётся полностью неизменным, операция является чистой функцией
  • Сохранение порядка: Отфильтрованные элементы сохраняют свой относительный порядок из исходного списка

Примеры использования:

// Фильтрация строк длиной не менее 3 символов
List<String> filteredStrings = GenericMethods.filter(strings, s -> s.length() >= 3);

// Фильтрация только положительных чисел
List<Integer> positiveNumbers = GenericMethods.filter(numbers, n -> n > 0);

Задание 3.3 - Reduce (Сокращение)

Ключевой код:

public static <T> T reduce(List<T> list, BinaryOperator<T> operator, T identity) {
    if (list == null || list.isEmpty()) {
        return identity;
    }
    T result = identity;
    for (T item : list) {
        result = operator.apply(result, item);
    }
    return result;
}

Комментарии к реализации:

  • Параметр identity: Нейтральный элемент операции (аккумулятор), обеспечивает корректную работу с пустыми списками и задаёт начальное значение
  • Итеративное применение операции: Бинарный оператор последовательно применяется ко всем элементам списка, накапливая результат
  • Защита от исключительных ситуаций: Гарантирует возврат identity для пустых или null списков, предотвращая NullPointerException
  • Функциональный интерфейс BinaryOperator: Определяет операцию сокращения как бинарную функцию, принимающую два аргумента и возвращающую результат

Примеры использования:

// Конкатенация всех строк списка
String concatenated = GenericMethods.reduce(strings, (s1, s2) -> s1 + s2, "");

// Вычисление суммы всех чисел списка
Integer sum = GenericMethods.reduce(numbers, (n1, n2) -> n1 + n2, 0);

Задание 3.4 - Collect (Коллекционирование)

Ключевой код:

public static <T, R> R collect(List<T> list,
                              Supplier<R> supplier,
                              BiConsumer<T, R> accumulator) {
    R result = supplier.get();
    for (T item : list) {
        accumulator.accept(item, result);
    }
    return result;
}

Комментарии к реализации:

  • Supplier<R>: Функциональный интерфейс, выступающий в роли фабрики для создания результирующей коллекции нужного типа
  • BiConsumer<T, R>: Определяет стратегию добавления отдельных элементов в результирующую коллекцию
  • Максимальная гибкость: Поддерживает создание любых типов коллекций с различными стратегиями накопления элементов
  • Разделение ответственности: Чёткое разделение между созданием коллекции (Supplier) и логикой добавления элементов (BiConsumer)

Примеры использования:

// Разбиение чисел на положительные и отрицательные
Map<Boolean, List<Integer>> partitioned = GenericMethods.collect(
    numbers,
    () -> {
        Map<Boolean, List<Integer>> map = new HashMap<>();
        map.put(true, new ArrayList<>());
        map.put(false, new ArrayList<>());
        return map;
    },
    (number, map) -> {
        if (number > 0) {
            map.get(true).add(number);
        } else {
            map.get(false).add(number);
        }
    }
);

Тестирование

Таблица тестирования Задания 1.2 - Универсальное хранилище

Тест-кейс Входные данные Ожидаемый результат Фактический результат Статус
Хранилище чисел с null Storage<Integer>(null) Альтернатива: 0 "Значение: 0"
Хранилище чисел со значением Storage<Integer>(99) Альтернатива: -1 "Значение: 99"
Хранилище строк с null Storage<String>(null) Альтернатива: "default" "Значение: default"
Хранилище строк со значением Storage<String>("hello") Альтернатива: "hello world" "Значение: hello"
Пользовательское числовое хранилище Ввод: пустая строка → альтернатива 0 "Значение: 0" Корректный вывод
Пользовательское строковое хранилище Ввод: "test" → альтернатива "default" "Значение: test" Корректный вывод

Таблица тестирования Задания 1.5 - Универсальная линия

Тест-кейс Входные данные Ожидаемый результат Фактический результат Статус
Создание 2D линии Points: (1,2) и (4,6) Длина: 5.0 "Длина 2D линии: 5.0"
Создание 3D линии Points: (1,2,3) и (4,6,9) Длина: ~7.68 "Длина 3D линии: 7.681..."
Попытка создать линию с null start = null Исключение IllegalArgumentException Корректное исключение
Попытка создать линию с одинаковыми точками Points: (1,1) и (1,1) Исключение IllegalArgumentException Корректное исключение
Интерактивное создание 2D линии Пользовательский ввод координат Корректный расчет длины Расчет соответствует формуле
Интерактивное создание 3D линии Пользовательский ввод координат Корректный расчет длины Расчет соответствует формуле

Таблица тестирования Задания 2.1 - Сдвиг линии

Тест-кейс Входные данные Ожидаемый результат Фактический результат Статус
Сдвиг обычной линии Line: (5,2)-(10,8) Начальная точка: (15,2) "Начальная точка после сдвига: {15.0;2.0}"
Сдвиг универсальной 3D линии LineGeneric: (5,2,1)-(10,8,3) Начальная точка: (15,2,1) "3D Линия после сдвига: Линия от {15.0;2.0;1.0}..."
Сдвиг линии с отрицательной координатой Line: (-7,3)-(2,9) Начальная точка: (3,3) "Координата X после сдвига: 3.0"
Интерактивный сдвиг Пользовательские координаты Увеличение X на 10 "Изменение координаты X: 10.0"

Таблица тестирования Задания 3.1 - Map

Тест-кейс Входные данные Преобразование Ожидаемый результат Фактический результат Статус
Строки → длины ["qwerty", "asdfg", "zx"] s -> s.length() [6, 5, 2] [6, 5, 2]
Числа → абсолютные значения [1, -3, 7] n -> Math.abs(n) [1, 3, 7] [1, 3, 7]
Массивы → максимумы [[1,5,3], [-2,8,0], [4,4,9,2]] arr -> Arrays.stream(arr).max() [5, 8, 9] [5, 8, 9]
Интерактивный: строки → длины Пользовательский ввод строк Преобразование в длины Корректные длины Соответствует вводу

Таблица тестирования Задания 3.2 - Filter

Тест-кейс Входные данные Условие фильтрации Ожидаемый результат Фактический результат Статус
Фильтрация строк по длине ["qwerty", "asdfg", "zx"] s -> s.length() >= 3 ["qwerty", "asdfg"] ["qwerty", "asdfg"]
Фильтрация положительных чисел [1, -3, 7] n -> n > 0 [1, 7] [1, 7]
Фильтрация массивов без положительных [[-1,-5,-3], [-2,8,0], [-4,-4,-9]] arr -> все элементы <= 0 [[-1,-5,-3], [-4,-4,-9]] Корректные массивы
Интерактивный: фильтрация чисел Пользовательские числа n -> n > 0 Только положительные Соответствует условию

Таблица тестирования Задания 3.3 - Reduce

Тест-кейс Входные данные Операция Identity Ожидаемый результат Фактический результат Статус
Конкатенация строк ["qwerty", "asdfg", "zx"] (s1,s2) -> s1+s2 "" "qwertyasdfgzx" "qwertyasdfgzx"
Сумма чисел [1, -3, 7] (n1,n2) -> n1+n2 0 5 5
Количество элементов в списках [[1,2,3], [4,5], [6,7,8,9]] map + reduce 0 9 9
Пустой список [] (s1,s2) -> s1+s2 "default" "default" "default"
Интерактивный: конкатенация Пользовательские строки Конкатенация "" Объединённая строка Корректная конкатенация

Таблица тестирования Задания 3.4 - Collect

Тест-кейс Входные данные Коллекция Логика накопления Ожидаемый результат Фактический результат Статус
Разбиение чисел [1, -3, 7] Map<Boolean, List> По знаку числа {true=[1,7], false=[-3]} Корректное разбиение
Группировка строк по длине ["qwerty", "asdfg", "zx", "qw"] Map<Integer, List> По длине строки {6=[qwerty], 5=[asdfg], 2=[zx, qw]} Корректная группировка
Уникальные строки ["qwerty", "asdfg", "qwerty", "qw"] Set<String> Добавление в Set ["qwerty", "asdfg", "qw"] Уникальные значения
Интерактивный: группировка Пользовательские строки Группировка по длине По длине Строки сгруппированы по длине Корректная группировка

About

Лабораторная работа 4. Вариант 1

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages