«1. Обзор
В этой статье мы кратко рассмотрим некоторые из наиболее интересных новых функций в Java 8.
Мы поговорим о: стандартных и статических методах интерфейса, справочнике по методам и необязательных.
Мы уже рассмотрели некоторые особенности выпуска Java 8 — потоковый API, лямбда-выражения и функциональные интерфейсы — поскольку это обширные темы, заслуживающие отдельного рассмотрения.
2. Интерфейс по умолчанию и статические методы
До Java 8 интерфейсы могли иметь только общедоступные абстрактные методы. Невозможно было добавить новую функциональность к существующему интерфейсу, не заставляя все реализующие классы создавать реализацию новых методов, а также невозможно было создать методы интерфейса с реализацией.
Начиная с Java 8, интерфейсы могут иметь статические методы и методы по умолчанию, которые, несмотря на то, что они объявлены в интерфейсе, имеют определенное поведение.
2.1. Статический метод
Рассмотрим следующий метод интерфейса (назовем этот интерфейс Vehicle):
static String producer() {
return "N&F Vehicles";
}
Статический метод производителя() доступен только через и внутри интерфейса. Он не может быть переопределен реализующим классом.
Для вызова вне интерфейса следует использовать стандартный подход для вызова статического метода:
String producer = Vehicle.producer();
2.2. Метод по умолчанию
Методы по умолчанию объявляются с использованием нового ключевого слова по умолчанию. Они доступны через экземпляр реализующего класса и могут быть переопределены.
Давайте добавим в наш интерфейс Vehicle метод по умолчанию, который также будет вызывать статический метод этого интерфейса:
default String getOverview() {
return "ATV made by " + producer();
}
Предположим, что этот интерфейс реализован классом VehicleImpl. Для выполнения метода по умолчанию необходимо создать экземпляр этого класса:
Vehicle vehicle = new VehicleImpl();
String overview = vehicle.getOverview();
3. Ссылки на методы
Ссылка на метод может использоваться как более короткая и более читаемая альтернатива лямбда-выражению, которое вызывает только существующий метод. Существует четыре варианта ссылок на методы.
3.1. Ссылка на статический метод
Ссылка на статический метод имеет следующий синтаксис: ContainingClass::methodName.
Попробуем подсчитать все пустые строки в List\u003cString\u003e с помощью Stream API.
boolean isReal = list.stream().anyMatch(u -> User.isRealUser(u));
Присмотритесь к лямбда-выражению в методе anyMatch(), оно просто вызывает статический метод isRealUser(User user) класса User. Поэтому его можно заменить ссылкой на статический метод:
boolean isReal = list.stream().anyMatch(User::isRealUser);
Такой код выглядит гораздо информативнее.
3.2. Ссылка на метод экземпляра
Ссылка на метод экземпляра имеет следующий синтаксис: containsInstance::methodName. Следующий код вызывает метод isLegalName(String string) типа User, который проверяет входной параметр:
User user = new User();
boolean isLegalName = list.stream().anyMatch(user::isLegalName);
3.3. Ссылка на метод экземпляра объекта определенного типа
Этот ссылочный метод имеет следующий синтаксис: ContainingType::methodName. Пример::
long count = list.stream().filter(String::isEmpty).count();
3.4. Ссылка на конструктор
Ссылка на конструктор имеет следующий синтаксис: ClassName::new. Поскольку конструктор в Java — это особый метод, к нему тоже можно применить ссылку на метод с помощью new в качестве имени метода.
Stream<User> stream = list.stream().map(User::new);
4. Optional\u003cT\u003e
До появления Java 8 разработчикам приходилось тщательно проверять значения, на которые они ссылались, из-за возможности создания исключения NullPointerException (NPE). Все эти проверки требовали довольно раздражающего и подверженного ошибкам шаблонного кода.
Необязательный класс Java 8\u003cT\u003e может помочь в ситуациях, когда существует вероятность получения NPE. Он работает как контейнер для объекта типа T. Он может возвращать значение этого объекта, если это значение не равно нулю. Когда значение внутри этого контейнера равно null, это позволяет выполнять некоторые предопределенные действия вместо создания NPE.
4.1. Создание Optional\u003cT\u003e
Экземпляр классаOptional может быть создан с помощью его статических методов:
Optional<String> optional = Optional.empty();
Возвращает пустой option.
String str = "value";
Optional<String> optional = Optional.of(str);
Возвращает необязательный параметр, содержащий ненулевое значение.
Optional<String> optional = Optional.ofNullable(getString());
«
«Возвращает необязательный параметр с определенным значением или пустой необязательный параметр, если параметр имеет значение null.
4.2. Необязательное использование \u003cT\u003e
List<String> list = getList();
List<String> listOpt = list != null ? list : new ArrayList<>();
Например, вы ожидаете получить List\u003cString\u003e и в случае null вы хотите заменить его новым экземпляром ArrayList\u003cString\u003e. С кодом до Java 8 вам нужно сделать что-то вроде этого:
List<String> listOpt = getList().orElseGet(() -> new ArrayList<>());
В Java 8 те же функциональные возможности могут быть достигнуты с помощью гораздо более короткого кода:
User user = getUser();
if (user != null) {
Address address = user.getAddress();
if (address != null) {
String street = address.getStreet();
if (street != null) {
return street;
}
}
}
return "not specified";
Там, где вам нужно, даже больше шаблонного кода добраться до поля какого-либо объекта по-старому. Предположим, у вас есть объект типа User, который имеет поле типа Address с полем street типа String. И по какой-то причине вам нужно вернуть значение поля улицы, если оно существует, или значение по умолчанию, если улица равна нулю:
Optional<User> user = Optional.ofNullable(getUser());
String result = user
.map(User::getAddress)
.map(Address::getStreet)
.orElse("not specified");
Это можно упростить с помощью опционального:
В этом примере мы использовали метод map() для преобразования результатов вызова getAdress() в Optional\u003cAddress\u003e и getStreet() в Optional\u003cString\u003e. Если бы какой-либо из этих методов вернул значение null, метод map() вернул бы пустой необязательный параметр.
Optional<OptionalUser> optionalUser = Optional.ofNullable(getOptionalUser());
String result = optionalUser
.flatMap(OptionalUser::getAddress)
.flatMap(OptionalAddress::getStreet)
.orElse("not specified");
Представьте, что наши геттеры возвращают Optional\u003cT\u003e. Таким образом, мы должны использовать метод flatMap() вместо map():
String value = null;
String result = "";
try {
result = value.toUpperCase();
} catch (NullPointerException exception) {
throw new CustomException();
}
Еще один вариант использования Optional — изменение NPE с другим исключением. Итак, как мы делали ранее, давайте попробуем сделать это в стиле, предшествующем Java 8:
String value = null;
Optional<String> valueOpt = Optional.ofNullable(value);
String result = valueOpt.orElseThrow(CustomException::new).toUpperCase();
А что, если мы воспользуемся Optional\u003cString\u003e? Ответ читабельнее и проще:
Обратите внимание, что как и для чего использовать Optional в вашем приложении — серьезное и спорное дизайнерское решение, и объяснение всех его плюсов и минусов выходит за рамки темы. эта статья. Если вам интересно, можете копнуть глубже, в интернете полно интересных статей, посвященных этой проблеме. Этот и этот другой могут быть очень полезными.
5. Заключение
В этой статье мы кратко обсудим некоторые интересные новые функции в Java 8.
Конечно, есть много других дополнений и улучшений, которые распределены по многим пакетам и классам Java 8 JDK.
Но информация, проиллюстрированная в этой статье, является хорошей отправной точкой для изучения и изучения некоторых из этих новых функций.