Создайте сущность Хранилище, которая обладает следующими характеристиками:
- Может хранить один произвольный объект в один момент времени.
- Хранилище неизменяемо.
- Объект кладется в Хранилище при его создании. В качестве объекта может быть сохранено также и значение null.
- Хранилище может вернуть ссылку на Объект.
- Если вместо объекта хранится null, необходимо вернуть какое-либо альтернативное значение.
- Метод получения значения должен работать с тем типом данных, который был указан во время создания объекта
Выполните следующие задания:
- Создайте Хранилище чисел, положите туда значение null. Передайте Хранилище в какой-либо метод, извлеките значение, и выведите его на экран. Альтернативой должно быть число 0.
- Создайте Хранилище чисел, положите туда значение 99. Передайте Хранилище в какой-либо метод, извлеките значение, и выведите его на экран. Альтернативой должно быть число -1.
- Создайте Хранилище строк, положите туда значение null. Передайте Хранилище в какой-либо метод, извлеките значение, и выведите его на экран. Альтернативой должна быть строка "default".
- Создайте Хранилище строк, положите туда значение "hello". Передайте Хранилище в какой-либо метод, извлеките значение, и выведите его на экран. Альтернативой должна быть строка "hello world".
Универсальная линия Измените сущность Линия из задачи 2.6.3, таким образом, чтобы
- При создании её объекта можно было точно указать тип точки, на которой расположена линия: двухмерная или трехмерная (из задачи 2.3.5).
- Методы получения и установки значения Точки также могли работать с типом указанным при создании объекта.
- Граница стирания не может быть хуже двумерной точки. Создайте и выведите на экран произвольную линию в трехмерном пространстве
Сдвинуть линию. Создайте метод, принимающий Линию из задачи 3.1.5 (с любой допустимой параметризацией) сдвигающей точку начала на 10 единиц по оси X. Например, если X был 5, то должен стать 15, если X был -7, то должен стать -17.
Функция. Разработайте такой метод, который будет принимать список значений типа T, и объект имеющий единственный метод apply. Данный метод надо применить к каждому элементу списка, и вернуть новый список значений типа P, при этом типы T и P могут совпадать, а могут не совпадать. Используйте разработанный метод следующим образом:
- Передайте в метод список со значениями:"qwerty", "asdfg", "zx", а получите список чисел, где каждое число соответствует длине каждой строки.
- Передайте в метод список со значениями: 1,-3,7, а получите список в котором все отрицательные числа стали положительными, а положительные остались без изменений
- Передайте в метод список состоящий из массивов целых чисел, а получите список в котором будут только максимальные значения каждого из исходных массивов
Фильтр. Разработайте такой метод, который будет принимать список значений типа T и объект имеющий единственный метод test (принимает T и возвращает boolean). Верните новый список типа T, из которого удалены все значения не прошедшие проверку условием. Используйте разработанный метод следующим образом:
- Передайте в метод список со значениями: "qwerty", "asdfg", "zx", и отфильтруйте все строки имеющие менее трех символов
- Передайте в метод список со значениями: 1,-3,7, и отфильтруйте все положительные элементы
- Передайте в метод список состоящий из массивов целых чисел, а получите список в котором будут только те массивы, в которых нет ни одного положительного элемента
Сокращение. Разработайте такой метод, который будет принимать список значений типа T и способ с помощью которого список значений можно свести к одному значению типа T, которое и возвращается из метода. Используйте разработанный метод следующим образом:
- Передайте в метод список со значениями: "qwerty", "asdfg", "zx", и сформируйте одну большую строку, которая состоит из всех строк исходного списка.
- Передайте в метод список со значениями: 1,-3,7, и верните сумму всех значений исходного списка.
- Имеется список, состоящий из списков целых чисел, получите общеe количество элементов во всех списках. Подсказка: решить задачу можно в одно действие или последовательно использовать методы из 3.3.1 и 3.3.3. Далее необходимо изменить разработанный метод таким образом, чтобы данный метод гарантированно не возвращал null и не выбрасывал ошибок в том случае, если исходный список пуст
Коллекционирование. Разработайте такой метод, который будет возвращать коллекцию типа P со значениями типа T. Данный метод будет принимать:
- Список исходных значений
- Способ создания результирующей коллекции
- Способ передачи значений исходного списка в результирующую коллекцию. Используйте разработанный метод следующим образом:
- Передайте в метод список со значениями: 1,-3,7, и верните их разбитыми на два подсписка, в одном из которых будут только положительные числа, а в другом только отрицательные.
- Передайте в метод список со значениями: "qwerty", "asdfg", "zx", "qw" и верните их разбитыми на подсписки таким образом, чтобы в любом подсписке были строки только одинаковой длины
- Передайте в метод список со значениями: "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
Ключевой код:
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);
}Назначение: Эти методы демонстрируют работу с различными специализациями хранилища и показывают использование разных альтернативных значений для разных типов данных.
Ключевой код:
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 формула) - Инкапсуляция данных: Геттеры возвращают копии точек для защиты от несанкционированного внешнего изменения внутреннего состояния
Ключевой код:
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() у всех используемых типов точек
Ключевой код:
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));Ключевой код:
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);Ключевой код:
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);Ключевой код:
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);
}
}
);| Тест-кейс | Входные данные | Ожидаемый результат | Фактический результат | Статус |
|---|---|---|---|---|
| Хранилище чисел с 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" | Корректный вывод | ✅ |
| Тест-кейс | Входные данные | Ожидаемый результат | Фактический результат | Статус |
|---|---|---|---|---|
| Создание 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 линии | Пользовательский ввод координат | Корректный расчет длины | Расчет соответствует формуле | ✅ |
| Тест-кейс | Входные данные | Ожидаемый результат | Фактический результат | Статус |
|---|---|---|---|---|
| Сдвиг обычной линии | 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" | ✅ |
| Тест-кейс | Входные данные | Преобразование | Ожидаемый результат | Фактический результат | Статус |
|---|---|---|---|---|---|
| Строки → длины | ["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] | ✅ |
| Интерактивный: строки → длины | Пользовательский ввод строк | Преобразование в длины | Корректные длины | Соответствует вводу | ✅ |
| Тест-кейс | Входные данные | Условие фильтрации | Ожидаемый результат | Фактический результат | Статус |
|---|---|---|---|---|---|
| Фильтрация строк по длине | ["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 |
Только положительные | Соответствует условию | ✅ |
| Тест-кейс | Входные данные | Операция | 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" | ✅ |
| Интерактивный: конкатенация | Пользовательские строки | Конкатенация | "" | Объединённая строка | Корректная конкатенация | ✅ |
| Тест-кейс | Входные данные | Коллекция | Логика накопления | Ожидаемый результат | Фактический результат | Статус |
|---|---|---|---|---|---|---|
| Разбиение чисел | [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"] | Уникальные значения | ✅ |
| Интерактивный: группировка | Пользовательские строки | Группировка по длине | По длине | Строки сгруппированы по длине | Корректная группировка | ✅ |