Optional: как правильно бороться с NPE? 🛡️Привет, коллеги! 👋
Сегодня разбираем
Optional — современный способ борьбы с NullPointerException.
Зачем нужен Optional?Раньше:public String getUserName(User user) {
if (user != null) {
return user.getName();
}
return null; // ❌ Риск NPE
}Сейчас:public Optional<String> getUserName(User user) {
return Optional.ofNullable(user)
.map(User::getName);
}Создание Optional// Пустой Optional
Optional<String> empty = Optional.empty();
// Optional с гарантированно не-null значением
Optional<String> nonNull = Optional.of("hello");
// Optional с возможным null
Optional<String> nullable = Optional.ofNullable(getName());
Основные методыПроверка наличия значения:Optional<String> opt = Optional.of("hello");
if (opt.isPresent()) { // Проверка
String value = opt.get(); // Получение (опасно!)
}
opt.ifPresent(value -> { // Безопасное выполнение
System.out.println(value);
});Извлечение значений:Optional<String> opt = Optional.ofNullable(getName());
// Значение по умолчанию
String result1 = opt.orElse("default");
// Вычисляемое значение по умолчанию
String result2 = opt.orElseGet(() -> generateDefault());
// Исключение, если значения нет
String result3 = opt.orElseThrow(() -> new RuntimeException("Not found"));
Цепочки вызовов с map() и flatMap()class User {
private Address address;
// getters
}
class Address {
private String street;
// getters
}
// Без Optional (ужас вложенных проверок)
public String getStreet(User user) {
if (user != null) {
Address address = user.getAddress();
if (address != null) {
return address.getStreet();
}
}
return null;
}
// С Optional (элегантно!)
public String getStreet(User user) {
return Optional.ofNullable(user)
.map(User::getAddress)
.map(Address::getStreet)
.orElse("Unknown street");
}Фильтрация и преобразованиеOptional<User> userOpt = findUserById(1);
// Фильтрация
Optional<User> adultUser = userOpt
.filter(user -> user.getAge() >= 18);
// Преобразование
Optional<String> userName = userOpt
.map(User::getName)
.filter(name -> name.length() > 3);
Правила использования✅
Правильно:// Как возвращаемое значение
public Optional<User> findUser(int id) {
return Optional.ofNullable(userRepository.findById(id));
}
// Для безопасной обработки цепочек
public void processUser(int id) {
findUser(id)
.map(User::getName)
.ifPresent(System.out::println);
}
❌
Неправильно:// Не используйте Optional в полях класса
private Optional<String> name; // ❌
// Не передавайте Optional как параметр
public void process(Optional<String> data) { // ❌
// Не используйте get() без проверки
String name = optional.get(); // ❌
Практические примеры// Валидация данных
public boolean isValidUser(User user) {
return Optional.ofNullable(user)
.map(User::getEmail)
.filter(email -> email.contains("@"))
.isPresent();
}
// Работа с коллекциями
public List<String> getActiveUserNames(List<User> users) {
return users.stream()
.map(user -> Optional.ofNullable(user)
.filter(User::isActive)
.map(User::getName)
.orElse("Unknown"))
.collect(Collectors.toList());
}
Частые вопросы на собеседованиях1.
В чем разница между map() и flatMap()?2.
Когда использовать orElse() vs orElseGet()?3.
Почему нельзя использовать Optional в полях класса?4.
Как обработать цепочку вызовов с Optional?5.
Какие есть альтернативы Optional?Производительность-
orElse() — вычисляется всегда, даже если значение есть
-
orElseGet() — вычисляется только при отсутствии значения
-
orElseThrow() — современная замена проверкам с исключениями