- Описывать простой класс данных через
recordи уместно использоватьvar - Писать лямбда-выражения и ссылки на методы
- Обрабатывать коллекции потоками (
filter,map,sorted) и объединить всё в проекте
Java уже больше 30 лет, но она не устарела: за последние годы в язык добавили возможности, которые заметно сокращают код. Класс, занимавший 40 строк, превращается в однострочный record, а пятистрочный цикл — в однострочный поток. В этом уроке ты познакомишься с этими возможностями, а затем объединишь всё, что изучил в курсе, в небольшой, но настоящий проект — журнал успеваемости.
record и var
Record (запись, с Java 16) — краткая форма класса, который хранит неизменяемые данные. Если написать record Point(int x, int y), Java сама создаст private final-поля, конструктор, методы x() и y(), а также equals, hashCode и toString. В запись можно добавлять и свои методы. А var позволяет компилятору самому определить тип локальной переменной.
public class Main {
public static void main(String[] args) {
Point a = new Point(3, 4);
Point b = new Point(3, 4);
System.out.println(a);
System.out.println(a.x() + a.y());
System.out.println(a.equals(b) + " " + (a == b));
System.out.println(a.distanceToOrigin());
var message = "Records save time";
var count = message.length();
System.out.println(message + " (" + count + " chars)");
}
}
record Point(int x, int y) {
double distanceToOrigin() {
return Math.sqrt(x * x + y * y);
}
}Point[x=3, y=4] 7 true false 5.0 Records save time (17 chars)
a.equals(b) дал true: записи сравниваются по содержимому. А a == b — false, потому что это два разных объекта. Обрати внимание: в именах геттеров нет get — не getX(), а просто x().
Лямбды и ссылки на методы
Лямбда-выражение — короткая функция без имени: n -> n * n означает «возьми n, верни n * n». Лямбды используются вместо функциональных интерфейсов — интерфейсов с единственным абстрактным методом: Predicate (проверяет условие), Function (преобразует значение), Comparator (сравнивает два объекта). Если лямбда только вызывает готовый метод, её можно записать ещё короче — ссылкой на метод: System.out::println.
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
import java.util.function.Predicate;
public class Main {
public static void main(String[] args) {
Predicate<Integer> isEven = n -> n % 2 == 0;
Function<Integer, Integer> square = n -> n * n;
System.out.println(isEven.test(4) + " " + square.apply(7));
List<String> names = new ArrayList<>(List.of("Murad", "Aysel", "Elvin", "Leyla"));
names.sort((a, b) -> a.compareTo(b));
names.removeIf(name -> name.startsWith("E"));
names.forEach(name -> System.out.print(name + " "));
System.out.println();
names.forEach(System.out::println);
}
}true 49 Aysel Leyla Murad Aysel Leyla Murad
Потоки (Stream API)
Поток (stream) обрабатывает элементы коллекции как конвейер: filter оставляет подходящие под условие, map преобразует каждый элемент, sorted сортирует, а toList, count, sum или average собирают результат. Поток не меняет исходную коллекцию. Код отвечает на вопрос «что сделать», а не «как», поэтому легко читается.
import java.util.List;
import java.util.stream.Collectors;
public class Main {
public static void main(String[] args) {
List<Integer> scores = List.of(78, 95, 62, 88, 45, 91);
List<Integer> passed = scores.stream()
.filter(s -> s >= 60)
.sorted()
.toList();
System.out.println(passed);
int total = scores.stream().mapToInt(Integer::intValue).sum();
double avg = scores.stream().mapToInt(Integer::intValue).average().orElse(0);
System.out.println(total + " " + avg);
String initials = List.of("Baku", "Ganja", "Shaki").stream()
.map(c -> c.substring(0, 1))
.collect(Collectors.joining("-"));
System.out.println(initials);
long count = scores.stream().filter(s -> s > 80).count();
System.out.println(count);
}
}[62, 78, 88, 91, 95] 459 76.5 B-G-S 3
Проект: журнал успеваемости
- 1Опиши данные
Ученик —
recordс именем и списком баллов. Его компактный конструктор отклоняет баллы вне диапазона 0–100 с помощьюIllegalArgumentException. - 2Добавь вычисления
average()находит средний балл с помощью потока и округляет его до одного знака, аletter()выставляет буквенную оценку switch-выражением. - 3Напиши класс журнала
GradeBookхранит учеников вprivateArrayList, печатает отсортированный отчёт и считает средний балл класса. - 4Собери всё в main
Добавь учеников, перехвати неверные данные через
try/catchи выведи отчёт.
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
public class Main {
public static void main(String[] args) {
GradeBook book = new GradeBook();
book.add(new Student("Aysel", List.of(92, 88, 95)));
book.add(new Student("Murad", List.of(85, 78, 84)));
book.add(new Student("Leyla", List.of(58, 49, 61)));
book.add(new Student("Nigar", List.of(70, 65, 72)));
try {
book.add(new Student("Elvin", List.of(90, 120)));
} catch (IllegalArgumentException e) {
System.out.println("Skipped: " + e.getMessage());
}
book.printReport();
System.out.println("Class average: " + book.classAverage());
}
}
record Student(String name, List<Integer> scores) {
Student {
for (int s : scores) {
if (s < 0 || s > 100) throw new IllegalArgumentException(name + " has score " + s);
}
}
double average() {
double avg = scores.stream().mapToInt(Integer::intValue).average().orElse(0);
return Math.round(avg * 10) / 10.0;
}
String letter() {
return switch ((int) average() / 10) {
case 10, 9 -> "A";
case 8 -> "B";
case 7 -> "C";
case 6 -> "D";
default -> "F";
};
}
}
class GradeBook {
private final List<Student> students = new ArrayList<>();
void add(Student s) { students.add(s); }
double classAverage() {
double avg = students.stream().mapToDouble(Student::average).average().orElse(0);
return Math.round(avg * 10) / 10.0;
}
void printReport() {
students.stream()
.sorted(Comparator.comparingDouble(Student::average).reversed())
.forEach(s -> System.out.println(s.name() + ": " + s.average() + " (" + s.letter() + ")"));
}
}Skipped: Elvin has score 120 Aysel: 91.7 (A) Murad: 82.3 (B) Nigar: 69.0 (D) Leyla: 56.0 (F) Class average: 74.8
| В проекте | Из какой темы |
|---|---|
record Student, Student { ... } | записи, конструкторы |
throw new IllegalArgumentException, try/catch | исключения |
switch ((int) average() / 10) | switch-выражения, приведение типов |
private final List<Student> | инкапсуляция, коллекции, дженерики |
stream(), Student::average, s -> ... | потоки, ссылки на методы, лямбды |
Student { ... } — компактный конструктор записи: он позволяет проверить данные, не повторяя параметры, а поля заполняются автоматически после него. Балл 120 у Эльвина не принят, и он не попал в журнал, но программа продолжила работу — как в уроке об исключениях.
Главное
recordодной строкой создаёт класс неизменяемых данных: поля, конструктор, геттеры,equalsиtoStringпоявляются автоматически.varпоручает компилятору вывести тип локальной переменной, но тип по-прежнему неизменен.- Лямбда (
x -> ...) — краткая реализация функционального интерфейса;Type::method— ссылка на метод. - Поток:
stream()→filter/map/sorted→toList,count,sum,average; исходная коллекция не меняется. - Настоящий проект объединяет все части курса: классы, исключения, коллекции и современную Java.
Проверь себя
Вопросов: 10. Каждый правильный ответ приносит XP.
x у record Point(int x, int y)?