1. Обзор
В этой статье мы кратко рассмотрим некоторые из наиболее интересных новых функций в Java 8.
Мы поговорим о: интерфейсных методах по умолчанию и статических методах, ссылочных и необязательных методах.
Мы уже рассмотрели некоторые функции выпуска Java 8 – потоковый API , лямбда – выражения и функциональные интерфейсы, поскольку они являются всеобъемлющими темами, которые заслуживают отдельного рассмотрения.
2. Интерфейс по умолчанию и статические методы
До Java 8 интерфейсы могли иметь только общедоступные абстрактные методы. Невозможно было добавить новую функциональность в существующий интерфейс, не заставляя все реализующие классы создавать реализацию новых методов, а также невозможно было создавать методы интерфейса с реализацией.
Начиная с Java 8, интерфейсы могут иметь методы static и default , которые, несмотря на объявление в интерфейсе, имеют определенное поведение.
2.1. Статический метод
Рассмотрим следующий метод интерфейса (назовем этот интерфейс Vehicle ):
static String producer() { return "N&F Vehicles"; }
Статический метод producer() доступен только через интерфейс и внутри него. Он не может быть переопределен реализующим классом.
Чтобы вызвать его вне интерфейса, следует использовать стандартный подход для вызова статического метода:
String producer = Vehicle.producer();
2.2. Метод по умолчанию
Методы по умолчанию объявляются с использованием ключевого слова new default //. Они доступны через экземпляр реализующего класса и могут быть переопределены.
Давайте добавим метод default в наш интерфейс Vehicle , который также вызовет метод static этого интерфейса:
default String getOverview() { return "ATV made by " + producer(); }
Предположим, что этот интерфейс реализован классом VehicleImpl. Для выполнения метода default должен быть создан экземпляр этого класса:
Vehicle vehicle = new VehicleImpl(); String overview = vehicle.getOverview();
3. Ссылки на методы
Ссылка на метод может использоваться в качестве более короткой и читаемой альтернативы для лямбда-выражения, которое вызывает только существующий метод. Существует четыре варианта ссылок на методы.
3.1. Ссылка на статический метод
Ссылка на статический метод содержит следующий синтаксис: Содержащий класс::имя метода.
Давайте попробуем подсчитать все пустые строки в List с помощью Stream API.
boolean isReal = list.stream().anyMatch(u -> User.isRealUser(u));
Взгляните поближе на лямбда-выражение в методе any Match () , оно просто вызывает статический метод является реальным пользователем(User user) класса User . Таким образом, его можно заменить ссылкой на статический метод:
boolean isReal = list.stream().anyMatch(User::isRealUser);
Этот тип кода выглядит гораздо более информативным.
3.2. Ссылка на метод экземпляра
Ссылка на метод экземпляра содержит следующий синтаксис: c содержащий экземпляр::имя метода. Следующий код вызывает метод является юридическим именем(строка string) типа User , который проверяет входной параметр:
User user = new User(); boolean isLegalName = list.stream().anyMatch(user::isLegalName);
3.3. Ссылка на Метод экземпляра Объекта определенного типа
Этот ссылочный метод использует следующий синтаксис: C содержащий тип::имя метода. Пример::
long count = list.stream().filter(String::isEmpty).count();
3.4. Ссылка на конструктор
Ссылка на конструктор принимает следующий синтаксис: className :: new. Поскольку конструктор в Java является специальным методом, ссылка на метод также может быть применена к нему с помощью new | в качестве имени метода .
Streamstream = list.stream().map(User::new);
4. Необязательно
До Java 8 разработчикам приходилось тщательно проверять значения, на которые они ссылались, из-за возможности создания исключения NullPointerException (NPE) . Все эти проверки требовали довольно раздражающего и подверженного ошибкам шаблонного кода.
Java 8 Необязательный класс может помочь справиться с ситуациями, когда есть возможность получить NPE . Он работает как контейнер для объекта типа T. Он может возвращать значение этого объекта, если это значение не является null . Когда значение внутри этого контейнера равно null , это позволяет выполнять некоторые предопределенные действия вместо того, чтобы выбрасывать NPE.
4.1. Создание необязательного
Экземпляр класса Optional может быть создан с помощью его статических методов:
Optionaloptional = Optional.empty();
Возвращает пустой Необязательный.
String str = "value"; Optionaloptional = Optional.of(str);
Возвращает Необязательный , содержащий ненулевое значение.
Optionaloptional = Optional.ofNullable(getString());
Возвращает Необязательный с определенным значением или пустой Необязательный , если параметр равен null.
4.2. Необязательное использование
Например, вы ожидаете получить List , а в случае null вы хотите заменить его новым экземпляром ArrayList. С кодом до Java 8 вам нужно сделать что-то вроде этого:
Listlist = getList(); List listOpt = list != null ? list : new ArrayList<>();
С Java 8 та же функциональность может быть достигнута с помощью гораздо более короткого кода:
ListlistOpt = getList().orElseGet(() -> new ArrayList<>());
Существует еще больше шаблонного кода, когда вам нужно добраться до поля какого-либо объекта старым способом. Предположим, у вас есть объект типа User , который имеет поле типа Address с полем s threat типа 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";
Это можно упростить с помощью Необязательно:
Optionaluser = Optional.ofNullable(getUser()); String result = user .map(User::getAddress) .map(Address::getStreet) .orElse("not specified");
В этом примере мы использовали метод map() для преобразования результатов вызова getAdress() в Необязательный<Адрес> и get Street() в Необязательный<Строка>. Если какой-либо из этих методов вернет null , метод map() вернет пустой Необязательный.
Представьте, что наши геттеры возвращают Необязательный. Таким образом, мы должны использовать метод flatMap() вместо map():
OptionaloptionalUser = Optional.ofNullable(getOptionalUser()); String result = optionalUser .flatMap(OptionalUser::getAddress) .flatMap(OptionalAddress::getStreet) .orElse("not specified");
Другой вариант использования Необязательно изменяется NPE с другим исключением. Итак, как и ранее, давайте попробуем сделать это в стиле до Java 8:
String value = null; String result = ""; try { result = value.toUpperCase(); } catch (NullPointerException exception) { throw new CustomException(); }
А что, если мы используем Необязательный ? Ответ более читабельный и простой:
String value = null; OptionalvalueOpt = Optional.ofNullable(value); String result = valueOpt.orElseThrow(CustomException::new).toUpperCase();
Обратите внимание, что то, как и с какой целью использовать Необязательно в вашем приложении, является серьезным и спорным дизайнерским решением, и объяснение всех его плюсов и минусов выходит за рамки этой статьи. Если вам интересно, вы можете копнуть глубже, в интернете есть много интересных статей, посвященных этой проблеме. Это одно и это другое было бы очень полезно.
5. Заключение
В этой статье мы кратко обсудим некоторые интересные новые функции в Java 8.
Конечно, есть много других дополнений и улучшений, которые распространяются на многие пакеты и классы Java 8 JDK.
Но информация, проиллюстрированная в этой статье, является хорошей отправной точкой для изучения и изучения некоторых из этих новых функций.
Наконец, весь исходный код статьи доступен на GitHub.