Новые функции в Java 8

Новые функции в Java 8

1. обзор

В этой статье мы кратко рассмотрим некоторые из наиболее интересных новых функций Java 8.

Мы поговорим о: интерфейсах по умолчанию и статических методах, справочниках по методам и Optional.

Мы уже рассмотрели некоторые особенности выпуска Java 8 -stream API,lambda expressions and functional interfaces - поскольку они представляют собой всеобъемлющие темы, заслуживающие отдельного рассмотрения.

2. Стандартные и статические методы интерфейса

До Java 8 интерфейсы могли иметь только публичные абстрактные методы. Невозможно было добавить новые функциональные возможности к существующему интерфейсу, не заставляя все реализующие классы создавать реализацию новых методов, и не было возможно создать методы интерфейса с реализацией.

Начиная с Java 8, интерфейсы могут иметь методыstatic иdefault, которые, несмотря на то, что объявлены в интерфейсе, имеют определенное поведение.

2.1. Статический метод

Рассмотрим следующий метод интерфейса (назовем этот интерфейсVehicle):

static String producer() {
    return "N&F Vehicles";
}

Статический методproducer() доступен только через интерфейс и внутри него. Он не может быть отменен классом реализации.

Чтобы вызвать его вне интерфейса, следует использовать стандартный подход для вызова статического метода:

String producer = Vehicle.producer();

2.2. Метод по умолчанию

Методы по умолчанию объявляются с использованием новогоdefault keyword. Они доступны через экземпляр реализующего класса и могут быть переопределены.

Давайте добавим методdefault к нашему интерфейсуVehicle, который также будет вызывать методstatic этого интерфейса:

default String getOverview() {
    return "ATV made by " + producer();
}

Предположим, что этот интерфейс реализован классомVehicleImpl.. Для выполнения методаdefault необходимо создать экземпляр этого класса:

Vehicle vehicle = new VehicleImpl();
String overview = vehicle.getOverview();

3. Ссылки на метод

Ссылка на метод может использоваться в качестве более короткой и удобочитаемой альтернативы лямбда-выражения, которое вызывает только существующий метод. Существует четыре варианта ссылок на методы.

3.1. Ссылка на статический метод

Ссылка на статический метод имеет следующий синтаксис:ContainingClass::methodName.

Давайте попробуем подсчитать все пустые строки вList<String> с помощью 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. Ссылка на метод экземпляра

Ссылка на метод экземпляра содержит следующий синтаксис:containingInstance::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 stream = list.stream().map(User::new);

4. Необязательно

До Java 8 разработчикам приходилось тщательно проверять значения, на которые они ссылались, из-за возможности выбросаNullPointerException (NPE). Все эти проверки требовали довольно раздражающего и подверженного ошибкам стандартного кода.

Класс Java 8Optional<T> может помочь справиться с ситуациями, когда есть возможность получитьNPE.. Он работает как контейнер для объекта типаT.. Он может возвращать значение этого объекта, если это значение неnull. Когда значение внутри этого контейнераnull, оно позволяет выполнять некоторые предопределенные действия вместо того, чтобы бросатьNPE.

4.1. СозданиеOptional<T>

Экземпляр классаOptional можно создать с помощью его статических методов:

Optional optional = Optional.empty();

Возвращает пустойOptional.

String str = "value";
Optional optional = Optional.of(str);

ВозвращаетOptional, которое содержит ненулевое значение.

Optional optional = Optional.ofNullable(getString());

ВернетOptional с определенным значением или пустойOptional, если параметрnull.

4.2. Optional<T> usageс

Например, вы ожидаете получитьList<String>, а в случаеnull вы хотите заменить его новым экземпляромArrayList<String>.Wс кодом до Java 8, который вы нужно сделать что-то вроде этого:

List list = getList();
List listOpt = list != null ? list : new ArrayList<>();

С Java 8 та же функциональность может быть достигнута с помощью гораздо более короткого кода:

List listOpt = getList().orElseGet(() -> new ArrayList<>());

Еще больше шаблонного кода, когда вам нужно добраться до поля какого-либо объекта старым способом. Предположим, у вас есть объект типаUser, который имеет поле типаAddress с полем street типаString. И по какой-то причине вам нужно вернуть значение полеstreet, если оно существует, или значение по умолчанию, еслиstreet равноnull:

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";

Это можно упростить с помощьюOptional:

Optional user = Optional.ofNullable(getUser());
String result = user
  .map(User::getAddress)
  .map(Address::getStreet)
  .orElse("not specified");

В этом примере мы использовали методmap() для преобразования результатов вызоваgetAdress() вOptional<Address> иgetStreet() вOptional<String>.. Если какой-либо из этих методов вернулnull методmap() вернет пустойOptional.

Представьте, что наши геттеры возвращаютOptional<T>. Итак, мы должны использовать методflatMap() вместоmap():

Optional optionalUser = Optional.ofNullable(getOptionalUser());
String result = optionalUser
  .flatMap(OptionalUser::getAddress)
  .flatMap(OptionalAddress::getStreet)
  .orElse("not specified");

Другой вариант использованияOptional - это изменениеNPE с другим исключением. Итак, как мы делали ранее, давайте попробуем сделать это в стиле до Java 8:

String value = null;
String result = "";
try {
    result = value.toUpperCase();
} catch (NullPointerException exception) {
    throw new CustomException();
}

А что, если использоватьOptional<String>? Ответ более читабелен и прост:

String value = null;
Optional valueOpt = Optional.ofNullable(value);
String result = valueOpt.orElseThrow(CustomException::new).toUpperCase();

Обратите внимание, что, как и с какой целью, чтобы использоватьOptional в вашем приложении является серьезным и противоречивым дизайнерским решением и объяснение всех его плюсов и минусов выходят за рамки данной статьи. Если вам интересно, вы можете копнуть глубже, в интернете есть множество интересных статей, посвященных этой проблеме. This one иthis another one могут быть очень полезны.

5. Заключение

В этой статье мы кратко обсудим некоторые интересные новые функции в Java 8.

Конечно, есть много других дополнений и улучшений, которые распространяются на многие пакеты и классы Java 8 JDK.

Но информация, представленная в этой статье, является хорошей отправной точкой для изучения и изучения некоторых из этих новых функций.

Наконец, доступен весь исходный код статьиover on GitHub.