Использование Optional для безопасности при работе с null
На этом этапе мы рассмотрим более современный подход к обработке потенциальных значений null
в Java с использованием класса Optional
, введенного в Java 8. Optional
представляет собой контейнерный объект, который может содержать или не содержать не-null
значение. Он предоставляет способ более явно представлять наличие или отсутствие значения, что может помочь уменьшить риск возникновения ошибок NullPointerException
.
Хотя проверки if (collection != null)
вполне допустимы и необходимы во многих ситуациях, использование Optional
может сделать ваш код более читаемым и выразительным, особенно при работе с методами, которые могут вернуть значение или null
.
Давайте посмотрим, как можно использовать Optional
с коллекцией. Хотя Optional
обычно используется для одиночных значений, вы можете столкнуться с ситуациями, когда метод возвращает Optional<List<SomeObject>>
.
-
Откройте файл HelloJava.java
в редакторе WebIDE.
-
Замените содержимое файла следующим кодом, который демонстрирует использование Optional
с потенциально null
списком:
import java.util.List;
import java.util.ArrayList;
import java.util.Optional; // Import Optional
public class HelloJava {
// A method that might return an Optional containing a list, or an empty Optional
public static Optional<List<String>> getNames(boolean includeNames) {
if (includeNames) {
List<String> names = new ArrayList<>();
names.add("Alice");
names.add("Bob");
return Optional.of(names); // Return an Optional containing the list
} else {
return Optional.empty(); // Return an empty Optional
}
}
public static void main(String[] args) {
// Case 1: Get names when includeNames is true
Optional<List<String>> namesOptional1 = getNames(true);
System.out.println("Checking namesOptional1:");
// Check if the Optional contains a value
if (namesOptional1.isPresent()) {
List<String> names = namesOptional1.get(); // Get the list from the Optional
System.out.println("List is present. Size: " + names.size());
// You can also check if the list itself is empty
if (names.isEmpty()) {
System.out.println("List is empty.");
} else {
System.out.println("List is not empty. First name: " + names.get(0));
}
} else {
System.out.println("List is not present (Optional is empty).");
}
System.out.println("---");
// Case 2: Get names when includeNames is false
Optional<List<String>> namesOptional2 = getNames(false);
System.out.println("Checking namesOptional2:");
if (namesOptional2.isPresent()) {
List<String> names = namesOptional2.get();
System.out.println("List is present. Size: " + names.size());
if (names.isEmpty()) {
System.out.println("List is empty.");
} else {
System.out.println("List is not empty. First name: " + names.get(0));
}
} else {
System.out.println("List is not present (Optional is empty).");
}
System.out.println("\nProgram finished.");
}
}
В этом коде:
- Мы определяем метод
getNames
, который возвращает Optional<List<String>>
. Этот метод имитирует ситуацию, когда вы можете получить список или ничего не получить (представленное пустым Optional
).
- В методе
main
мы вызываем getNames
с параметрами true
и false
, чтобы протестировать оба случая.
- Мы используем
namesOptional.isPresent()
для проверки, содержит ли Optional
список.
- Если
isPresent()
возвращает true
, мы используем namesOptional.get()
для извлечения списка. Это безопасно, так как мы уже проверили наличие значения.
- Внутри блока
isPresent()
мы можем выполнить проверки на сам список, например, names.isEmpty()
.
-
Сохраните файл.
-
Скомпилируйте программу в терминале:
javac HelloJava.java
-
Запустите программу:
java HelloJava
Вы должны увидеть вывод, похожий на следующий:
Checking namesOptional1:
List is present. Size: 2
List is not empty. First name: Alice
---
Checking namesOptional2:
List is not present (Optional is empty).
Program finished.
Этот вывод показывает, как Optional
помогает нам обрабатывать случай, когда список может не быть возвращен вообще.
Optional
также предоставляет другие полезные методы для обработки отсутствия значения, такие как:
orElse(defaultValue)
: Возвращает значение, если оно присутствует, в противном случае возвращает значение по умолчанию.
orElseGet(supplier)
: Возвращает значение, если оно присутствует, в противном случае возвращает результат функции supplier
.
orElseThrow(exceptionSupplier)
: Возвращает значение, если оно присутствует, в противном случае выбрасывает исключение, созданное функцией exceptionSupplier
.
ifPresent(consumer)
: Выполняет заданное действие, если значение присутствует.
Давайте изменим код, чтобы использовать ifPresent
для более компактного способа обработки списка, когда он присутствует.
-
Откройте файл HelloJava.java
в редакторе.
-
Измените метод main
, чтобы использовать ifPresent
:
import java.util.List;
import java.util.ArrayList;
import java.util.Optional;
public class HelloJava {
public static Optional<List<String>> getNames(boolean includeNames) {
if (includeNames) {
List<String> names = new ArrayList<>();
names.add("Alice");
names.add("Bob");
return Optional.of(names);
} else {
return Optional.empty();
}
}
public static void main(String[] args) {
// Case 1: Get names when includeNames is true
Optional<List<String>> namesOptional1 = getNames(true);
System.out.println("Checking namesOptional1 using ifPresent:");
namesOptional1.ifPresent(names -> {
// This block only runs if namesOptional1 contains a list
System.out.println("List is present. Size: " + names.size());
if (names.isEmpty()) {
System.out.println("List is empty.");
} else {
System.out.println("List is not empty. First name: " + names.get(0));
}
});
if (!namesOptional1.isPresent()) { // Still need a check if you need to handle the absence case
System.out.println("List is not present (Optional is empty).");
}
System.out.println("---");
// Case 2: Get names when includeNames is false
Optional<List<String>> namesOptional2 = getNames(false);
System.out.println("Checking namesOptional2 using ifPresent:");
namesOptional2.ifPresent(names -> {
System.out.println("List is present. Size: " + names.size());
if (names.isEmpty()) {
System.out.println("List is empty.");
} else {
System.out.println("List is not empty. First name: " + names.get(0));
}
});
if (!namesOptional2.isPresent()) {
System.out.println("List is not present (Optional is empty).");
}
System.out.println("\nProgram finished.");
}
}
Мы заменили структуру if (namesOptional.isPresent()) { ... namesOptional.get() ... }
на namesOptional.ifPresent(names -> { ... })
. Код внутри лямбда-выражения (names -> { ... }
) будет выполняться только в том случае, если Optional
содержит значение. Мы все еще добавили проверку if (!namesOptional.isPresent())
для обработки случая, когда Optional
пуст, так как ifPresent
обрабатывает только случай наличия значения.
-
Сохраните файл.
-
Скомпилируйте программу:
javac HelloJava.java
-
Запустите программу:
java HelloJava
Вывод должен быть таким же, как и раньше, что демонстрирует, что ifPresent
предоставляет альтернативный способ обработки наличия значения в Optional
.
Использование Optional
может сделать намерения вашего кода более ясными в отношении того, может ли значение отсутствовать, и поощряет вас явно обрабатывать такое отсутствие, уменьшая вероятность непредвиденных ошибок NullPointerException
.