Что нового в java 8
Перейти к содержимому

Что нового в java 8

  • автор:

Новые возможности в Java 8

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

Ссылка на метод экземпляра содержит синтаксис , содержащийInstance::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 в качестве имени метода:

 StreamUser> stream = list.stream().map(User::new); 

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

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

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

4.1. Создание необязательного ​

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

Давайте посмотрим, как вернуть пустой option :

 OptionalString> optional = Optional.empty(); 

Затем мы возвращаем необязательный элемент, содержащий ненулевое значение:

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

Наконец, вот как вернуть необязательный параметр с определенным значением или пустой необязательный параметр, если параметр имеет значение null :

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

4.2. Необязательное использование ​

Допустим, мы ожидаем получить List , и в случае null мы хотим заменить его новым экземпляром ArrayList .

С кодом до Java 8 нам нужно сделать что-то вроде этого:

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

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

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

Это можно упростить с помощью опционального :

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

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

Теперь представьте, что наши геттеры возвращают Optional .

В этом случае мы должны использовать метод flatMap() вместо map() :

 OptionalOptionalUser> 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 value = null;   OptionalString> valueOpt = Optional.ofNullable(value);   String result = valueOpt.orElseThrow(CustomException::new).toUpperCase(); 

Обратите внимание, как и для чего использовать Optional в нашем приложении — это серьезное и спорное дизайнерское решение, и объяснение всех его плюсов и минусов выходит за рамки этой статьи. Но есть много интересных статей, посвященных этой проблеме. Этот и этот могут быть очень полезными, чтобы копнуть глубже.

5. Вывод

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

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

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

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

Java 8. Новые возможности

Java SE 8 представляет самые глубокие изменения в языке Java и библиотеках с момента возникновения. Хорошей новостью является то, что эти изменения позволят Вам писать программы более легко. Java 8 предлагает новый интерфейс прикладного программирования — API (Streams API), который поддерживает множество параллельных операций обработки данных и помогает вам думать о них в представлениях похожих на запросы к базе данных. Изменение программирования в Java на функциональный стиль поможет Вам писать код, который является более кратким и выразительным, и (во многих случаях) готовым к использованию в параллельной среде. В этом курсе, вы узнаете, как, казалось бы, незначительные изменения, которые вводят лямбда-выражения делают это возможным.

После изучения курса вы сможете:

  • Применять лямбда-выражения (lambda expressions).
  • Использовать методы интерфейсов по умолчанию (default interface methods).
  • Включать в код ссылки на методы (method references) и повторяемые аннотации (repeatable annotations).
  • Встраивать новые конкурентные функции лямбда-выражений.
  • Работать с новыми возможностями API для работы с датами: Java Date и Time API.
  • Использовать среду Nashorn JavaScript.

Содержание курса

Модуль 1: Введение в лямбда-выражения.

  • Цель использования анонимного внутреннего класса.
  • Недостатки анонимных внутренних классов.
  • Компоненты лямбда-выражения.
  • Функциональный интерфейс.
  • Создание программ, использующих лямбда-выражения.

Модуль 2: Рефакторинг кода для использования лямбда-выражений.

  • Причины добавления лямбда-выражений в язык Java.
  • Рефакторинг кода для уменьшения избыточности.
  • Рефакторинг кода при использовании внутренних классов.
  • Рефакторинг кода при использовании лямбда-выражений.
  • Список преимуществ лямбда-выражений.

Модуль 3: Фильтрация коллекций с помощью лямбда-выражений.

  • Итерации по коллекции с помощью forEach.
  • Итерации по коллекции с помощью лямбда-выражений.
  • Описание интерфейса Stream.
  • Фильтрация коллекции с использованием лямбда-выражения.
  • Вызов существующего метода с использованием ссылки на метод.
  • Сцепление нескольких методов вместе.
  • Сравнение функционального и императивного программирования.
  • Определение конвейеров в терминах лямбда-выражений и коллекций.

Модуль 4: Использование встроенных интерфейсов в лямбда-выражениях.

  • Список встроенных интерфейсов, включенных в java.util.function.
  • Использование интерфейса Predicate.
  • Обработка объекта с помощью интерфейса Consumer.
  • Обработка одного объекта и возврат другого с помощью интерфейса Function.
  • Создание нового объекта с помощью интерфейса Supplier.
  • Использование примитивных версий базовых интерфейсов.
  • Использование бинарных версий базовых интерфейсов.

Модуль 5: Операции с коллекциями в лямбда-выражениях.

  • Извлечение данных из объекта с использованием отображения (map).
  • Обнаружение данных с использованием методов поиска.
  • Описание типов потоковых операций.
  • Определение класса Optional.
  • Выполнение расчетов с использованием методов.
  • Описание отложенной обработки.
  • Сортировка в потоке.
  • Сохранение результатов в коллекцию с помощью метода collect.

Модуль 6: Параллельные потоки.

  • Обзор ключевых характеристик потоков.
  • Сравнение старого стиля циклических операции с потоками.
  • Описание параллельного выполнения потокового процесса.
  • Список основных условий использования параллельных потоков.
  • Определение редукции (reduction).
  • Ассоциативная функция при редукции.
  • Вычисление значения при редукции.
  • Описание процесса декомпзиции и объединения при завершении.

Модуль 7: Примеры применения лямбда-выражений.

  • Изменение списка с использованием removeIf.
  • Обновление списка с использованием ReplaceAll.
  • Обновление map (отображение) с помощью computeIfAbsent, computeIfPresent и merge.
  • Отправка ключей и значений из map в stream.
  • Чтение файла в поток.
  • Чтение текстового файла в ArrayList.
  • Список, проход и поиск по структуре каталогов при помощи потока.
  • Сведение потока с помощью flatMap.

Модуль 8: Расширения методов.

  • Построение качественных библиотек.
  • Использование статических методов в интерфейсах.
  • Использование методов по умолчанию.
  • Правила наследования для методов по умолчанию.

Модуль 9: Использование DateTime API.

  • Список целей Date/Time API (JSR-310).
  • Создание и управление событиями на основе даты.
  • Создание и управление событиями на основе времени .
  • Комбинирование даты и времени в одном объекте.

Модуль 10: JavaScript на Java с использованием Nashorn.

  • Создание и выполнение сценариев shell.
  • Написание приложений JavaScript.
  • Написание JavaFX приложений с помощью JavaScript.

Разработчики java приложений, использующие Java SE 8.

Что нового в Java? Обзор нововведений Java JDK 8-15.

Play

date

01.01.1960

time

19:00 — 21:00

Поделитесь материалом в соцсетях!
Рейтинг: 5 . Проголосовало: 5
Вы проголосовали:
Для голосования нужно авторизироваться

После 2017 года Java стремительно обновлялась и каждые пол года выпускались новые версии.

На вебинаре мы обсудим, стоит ли переходить на самую последнюю версию, или лучше остаться на версии Java 8. Посмотрим новые возможности версий 9, 10, 11, 12, 13, 14, 15 и чем они полезны могут быть разработчику, также попробуем определиться с тем, какая из версий лучше для разработки на Java в 2021 году.

План вебинара:

  1. Почему Java 8 самая стабильная версия? Разница между Java 8 и Java 7.
  2. Java 9, небольшие изменения. Collections.
  3. Java 10, добавления var переменной.
  4. Java 11 как самая стабильная версия среди нововведений. Чтение из файла.
  5. Java 11. HttpClient. String API. Optional.
  6. Java 12 Компактно форматированный ввод. Изменение оператора switch.
  7. Java 13 Switch с возвращаемым значением. Демо-версия текстовых блоков.
  8. Java 14 NPE модифицированный.
  9. Java 15. Полная версия текстовых блоков.
  10. Java 15 instanceof модифицированный.
  11. Java 15 sealed classes.
  12. Java 15 Records.
  13. Подведение итогов по новым версиям Java.
  14. Ответы на вопросы.

Целевая аудитория:

  • Вебинары по схожей тематике
  • Видеоуроки по схожей тематике
  • Статьи по схожей тематике

Боговик Максим

date

01.01.1960

time

120

view

1265

Вадим Кожевников

date

01.01.1960

time

120

view

851

Волкова Мария

date

01.01.1960

time

120

view

700

Дмитрий Охрименко

date

01.01.1960

time

120

view

1869

Панченко Константин

date

01.01.1960

time

120

view

2087

Дмитрий Красный

date

01.01.1960

time

120

view

1510

Татьяна Доморадова

date

01.01.1960

time

120

view

997

Татьяна Доморадова

date

01.01.1960

time

120

view

1268

Виктория Чабан

date

01.01.1960

time

120

view

1850

Дмитрий Красный

date

01.01.1960

time

120

view

1733

Заречная Оксана

date

01.01.1960

time

120

view

1055

Дарья Галенко

date

01.01.1960

time

120

view

1127

2 ч 33 м 7 уроков
28 ч 47 м 15 уроков
7 ч 24 м 9 уроков
4 ч 49 м 10 уроков
17 ч 19 м 14 уроков
3 ч 58 м 7 уроков
20 ч 14 м 15 уроков
7 ч 56 м 12 уроков
5 ч 7 м 10 уроков
18 ч 50 м 12 уроков
9 ч 30 м 24 урока
2 ч 44 м 5 уроков

ТОП-10 лучших видео по Java от ITVDN

Читать подробнее

Сегодня вас ожидает подборка классных, наполненных полезным контентом видео от ITVDN по направлению Java. Топ насыщен как теоретическими видеоматериалами, которые помогают выстроить эффективное обучение, так и практическими вебинарами, ориентированными на прокачку навыков программирования при помощи джавы и сопутствующих технологий (фреймворк Spring, Elasticsearch). Итак, какие видео вошли в данный рейтинг?

Java vs JavaScript

Читать подробнее

Друзья, мы продолжаем серию статей, в которых сравниваем разные языки программирования. Первая статья была посвящена Python и Java — двум достаточно мощным и универсальным языкам, без которых тяжело представить современную разработку. Вооружившись объективностью и непредвзятостью, мы исследовали наиболее важные (для новичков) особенности каждого из них, а вам предоставили все результаты в компактном и удобном виде.

250+ вопросов по Java для Junior, Middle, Senior

Читать подробнее

Редакция DOU.ua обратилась к СТО и опытным специалистам, которые проводят интервью по направлению Java, о том, какие теоретические вопросы задают и какие задачи и задания предлагают решить кандидатам. Ведь, готовясь к техническому собеседованию, важно иметь четкий план. На основе полученных ответов составлена эта подборка вопросов с градацией по уровню специалиста. Надеемся, этот список вопросов станет своеобразным путеводителем, который поможет получить желаемую должность.

Кто такой Full-stack разработчик

Читать подробнее

Full-stack разработчик (произносится “фулл стек”) — это некий мастер на все руки в мире веб-разработки. Ему под силу реализовать как клиентскую, так и серверную сторону приложения, которыми, обычно, занимаются FrontEnd и BackEnd разработчики раздельно друг от друга. Таким образом, Full-stack специалист способен в одиночку вести проект от начала до конца.

Что должен знать Java разработчик в 2020 году?

Читать подробнее

В этот раз в нашей рубрике “Что должен знать разработчик. ” под прицелом оказался такой многофункциональный язык программирования, как Java. Мы рассмотрим основные причины популярности этого языка программирования, а также перечень технологий, которыми должен владеть специалист, который выбрал для себя это направление. Не будем медлить — начнем!

ТОП 20 тестовых заданий на интервью для Java разработчика

Читать подробнее

В этой статье мы приведем достаточно большое количество примеров программ из тех, что просят написать соискателей во время прохождения интервью на вакансию Java разработчика. Указанные тестовые задания мы приводим с реальными примерами кода, заданными в интервью как начинающих, так и достаточно опытных кандидатов.

Junior Java Developer – вопросы на собеседовании

Читать подробнее

В этой статье мы рассмотрим 25 наиболее часто встречающихся вопросов на интервью для новичков в программировании на Java. Все это реальные вопросы на собеседовании Java Junior Developer.

Kotlin vs Java: что лучше для Android-разработки?

Читать подробнее

Kotlin – это статически типизированный язык программирования, разработанный компанией JetBrains. Подобно языку Java, Kotlin стал отличным выбором для разработки приложений на Android. Это можно увидеть даже из того факта, что Android Studio поставляется со встроенной поддержкой Kotlin, как и с поддержкой Java.

Настоящий отладчик в JavaScript? Легко!

Читать подробнее

Конечно, console.log может рассказать вам о многом, но все же сказать, что это отладчик, все равно что сказать, что Канада – это США. Для полноценной отладки вам необходимо отдельное специализированное полнофункциональное приложение. Новый отладчик Firefox позволит вам легко писать быстрый, стабильный код. Вот как это работает.

C# против Java: какой язык программирования общего назначения выбрать?

Читать подробнее

Важно помнить, что C# берет свое начало в желании Microsoft иметь собственный «Java-подобный» язык для платформы .NET. Поскольку C# не создавался в вакууме, новые функции были добавлены и настроены для решения проблем, с которыми сталкивались разработчики Microsoft, когда они изначально пытались создать свою платформу на Visual J++. В то же время сообщество Java с открытым исходным кодом продолжало расти и между этими двумя языками развивалась гонка технических вооружений. Вот некоторые из основных различий между C# и Java.

Java Developer: планируем обучение правильно

Читать подробнее

Ваша цель — стать Junior Java developer в кратчайшие сроки? Что, если мы скажем вам, что её можно достичь всего за шесть месяцев? Хотите узнать — как? Внимательно читайте статью и следуйте рекомендациям.

Покупай подписку с доступом ко всем курсам и сервисам

Библиотека современных IT знаний в удобном формате

Выбирай свой вариант подписки в зависимости от задач, стоящих перед тобой. Но если нужно пройти полное обучение с нуля до уровня специалиста, то лучше выбирать Базовый или Премиум. А для того чтобы изучить 2-3 новые технологии, или повторить знания, готовясь к собеседованию, подойдет Пакет Стартовый.

  • Все видеокурсы на 3 месяца
  • Тестирование по 10 курсам
  • Проверка 5 домашних заданий
  • Консультация с тренером 30 мин
  • Все видеокурсы на 6 месяцев
  • Тестирование по 16 курсам
  • Проверка 10 домашних заданий
  • Консультация с тренером 60 мин
  • Все видеокурсы на 1 год
  • Тестирование по 24 курсам
  • Проверка 20 домашних заданий
  • Консультация с тренером 120 мин

Новое в Java 8

Добро пожаловать в ведение по Java 8. Этот материал шаг за шагом познакомит вас со всеми новыми фичами языка. Вы научитесь использовать методы интерфейсов по умолчанию (default interface methods), лямбда-выражения (lambda expressions), ссылки на методы (method references) и повторяемые аннотации (repeatable annotations). Все это будет сопровождаться короткими и простыми примерами кода. В конце статьи вы познакомитесь с наиболее свежими изменениями в API, касающихся потоков, функциональных интерфейсов, расширений для ассоциативных массивов, а также с изменениями в API работы с датами.

Методы интерфейсов по умолчанию

Java 8 позволяет вам добавлять неабстрактные реализации методов в интерфейс, используя ключевое слово default . Эта фича также известна, как методы расширения. Вот наш первый пример:

interface Formula < double calculate(int a); default double sqrt(int a) < return Math.sqrt(a); >> 

Кроме абстрактного метода calculate интерфейс Formula также определяет метод по умолчанию sqrt . Классы, имплементирующие этот интерфейс, должны переопределить только абстрактный метод calculate . Метод по умолчанию sqrt будет доступен без переопределения.

Formula formula = new Formula() < @Override public double calculate(int a) < return sqrt(a * 100); >>; formula.calculate(100); // 100.0 formula.sqrt(16); // 4.0 

formula реализован как анонимный объект. Этот код довольно избыточен: целых 6 строчек для такого простого вычисления как sqrt(a * 100) . В следующем разделе мы увидим, что в Java 8 есть куда более изящный способ реализации объектов с одним методом.

Лямбда-выражения

Давайте начнем с простого примера: сортировка массива строк в предыдущих версиях языка.

List names = Arrays.asList("peter", "anna", "mike", "xenia"); Collections.sort(names, new Comparator() < @Override public int compare(String a, String b) < return b.compareTo(a); >>); 

Статический метод Collections.sort принимает список и компаратор, который используется для сортировки списка. Наверняка вам часто приходилось создавать анонимные компараторы для того чтобы передать их в метод.

Java 8 предоставляет гораздо более короткий синтаксис — лямбда-выражения, чтобы вам не приходилось тратить время на создание анонимных объектов:

Collections.sort(names, (String a, String b) -> < return b.compareTo(a); >); 

Как видите, код гораздо короче и куда более читаем. И его можно сделать еще короче:

Collections.sort(names, (String a, String b) -> b.compareTo(a)); 

Для однострочных методов вы можете опустить скобки <> и ключевое слово return . Так еще короче:

Collections.sort(names, (a, b) -> b.compareTo(a)); 

Компилятору известны типы параметров, поэтому их можно тоже опустить. Давайте посмотрим, как еще могут использовать лямбда-выражения.

Функциональные интерфейсы

Как лямбда-выражения соответствуют системе типов языка Java? Каждой лямбде соответствует тип, представленный интерфейсом. Так называемый функциональный интерфейс должен содержать ровно один абстрактный метод. Каждое лямбда-выражение этого типа будет сопоставлено объявленному методу. Также, поскольку методы по умолчанию не являются абстрактными, вы можете добавлять в функциональный интерфейс сколько угодно таких методов.

Мы можем использовать какие угодно интерфейсы для лямбда-выражений, содержащие ровно один абстрактный метод. Для того, чтобы гарантировать, что ваш интерфейс отвечает этому требованию, используется аннотация @FunctionalInterface . Компилятор осведомлен об этой аннотации, и выдаст ошибку компиляции, если вы добавите второй абстрактный метод в функциональный интерфейс.

@FunctionalInterface interface Converter

Converter converter = (from) -> Integer.valueOf(from); Integer converted = converter.convert("123"); System.out.println(converted); // 123 

Имейте в виду, что этот код останется корректным даже если убрать аннотацию @FunctionalInterface .

Ссылки на методы и конструкторы

Предыдущий пример можно упростить, если использовать статические ссылки на методы:

Converter converter = Integer::valueOf; Integer converted = converter.convert("123"); System.out.println(converted); // 123 

Java 8 позволяет вам передавать ссылки на методы или конструкторы. Для этого нужно использовать ключевое слово :: . Предыдущий пример иллюстрирует передачу ссылки на статический метод. Однако мы также можем ссылаться на экземплярный метод:

class Something < String startsWith(String s) < return String.valueOf(s.charAt(0)); >> 
Something something = new Something(); Converter converter = something::startsWith; String converted = converter.convert("Java"); System.out.println(converted); // "J" 

Давайте посмотрим, как передавать ссылки на конструкторы. Сперва определим бин с несколькими конструкторами:

class Person < String firstName; String lastName; Person() <>Person(String firstName, String lastName) < this.firstName = firstName; this.lastName = lastName; >> 

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

interface PersonFactory

Теперь вместо реализации интерфейса мы соединяем все вместе при помощи ссылки на конструктор:

PersonFactory personFactory = Person::new; Person person = personFactory.create("Peter", "Parker"); 

Мы создаем ссылку на конструктор с помощью Person::new . Компилятор автоматически выбирает подходящий конструктор, сигнатура которого совпадает с сигнатурой PersonFactory.create .

Области действия лямбд

Доступ к переменным внешней области действия из лямбда-выражения очень схож к доступу из анонимных объектов. Вы можете ссылаться на переменные, объявленные как final , на экземплярные поля класса и статические переменные.

final int num = 1; Converter stringConverter = (from) -> String.valueOf(from + num); stringConverter.convert(2); // 3 

Но в отличии от анонимных объектов, переменная num не обязательно должна быть объявлена как final . Такой код тоже сработает:

int num = 1; Converter stringConverter = (from) -> String.valueOf(from + num); stringConverter.convert(2); // 3 

Однако переменная num должна все равно оставаться неизменяемой. Следующий код не скомпилируется:

int num = 1; Converter stringConverter = (from) -> String.valueOf(from + num); num = 3; 

Запись в переменную num в пределах лямбда-выражения также запрещена.

Доступ к полям и статическим переменным

В отличии от локальных переменных, мы можем записывать значения в экземплярные поля класса и статические переменные внутри лямбда-выражений. Это поведение хорошо знакомо по анонимным объектам.

class Lambda4 < static int outerStaticNum; int outerNum; void testScopes() < ConverterstringConverter1 = (from) -> < outerNum = 23; return String.valueOf(from); >; Converter stringConverter2 = (from) -> < outerStaticNum = 72; return String.valueOf(from); >; > > 
Доступ к методам интерфейсов по умолчанию

Помните пример с формулой из первого раздела? В интерфейсе Formula определен метод по умолчанию sqrt , который доступен из каждой имплементации интерфейса, включая анонимные объекты. Однако это не сработает в лямбда-выражениях.

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

Formula formula = (a) -> sqrt( a * 100); 
Встроенные функциональные интерфейсы

JDK 1.8 содержит множество встроенных функциональных интерфейсов. Некоторые из них хорошо известны по предыдущим версиям языка, например, Comparator или Runnable . Все эти интерфейсы были поддержаны в лямбдах добавлением аннотации @FunctionalInterface .

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

Предикаты

Предикаты — это функции, принимающие один аргумент, и возвращающие значение типа boolean. Интерфейс содержит различные методы по умолчанию, позволяющие строить сложные условия ( and , or , negate ).

Predicate predicate = (s) -> s.length() > 0; predicate.test("foo"); // true predicate.negate().test("foo"); // false Predicate nonNull = Objects::nonNull; Predicate isNull = Objects::isNull; Predicate isEmpty = String::isEmpty; Predicate isNotEmpty = isEmpty.negate(); 
Функции

Функции принимают один аргумент и возвращают некоторый результат. Методы по умолчанию могут использоваться для построения цепочек вызовов ( compose , andThen ).

Function toInteger = Integer::valueOf; Function backToString = toInteger.andThen(String::valueOf); backToString.apply("123"); // "123" 
Поставщики

Поставщики (suppliers) предоставляют результат заданного типа. В отличии от функций, поставщики не принимают аргументов.

Supplier personSupplier = Person::new; personSupplier.get(); // new Person 
Потребители

Потребители (consumers) представляют собой операции, которые производятся на одним входным аргументом.

Consumer greeter = (p) -> System.out.println("Hello, " + p.firstName); greeter.accept(new Person("Luke", "Skywalker")); 
Компараторы

Компараторы хорошо известны по предыдущим версиям Java. Java 8 добавляет в интерфейс различные методы по умолчанию.

Comparator comparator = (p1, p2) -> p1.firstName.compareTo(p2.firstName); Person p1 = new Person("John", "Doe"); Person p2 = new Person("Alice", "Wonderland"); comparator.compare(p1, p2); // > 0 comparator.reversed().compare(p1, p2); // < 0 
Опциональные значения

Опциональные значения (optionals) не являются функциональными интерфейсами, однако являются удобным средством предотвращения NullPointerException. Это важная концепция, которая понадобится нам в следующем разделе, поэтому давайте взглянем, как работают опциональные значения.

Опциональные значение — это по сути контейнер для значения, которое может быть равно null. Например, вам нужен метод, который возвращает какое-то значение, но иногда он должен возвращать пустое значение. Вместо того, чтобы возвращать null, в Java 8 вы можете вернуть опциональное значение.

Optional optional = Optional.of("bam"); optional.isPresent(); // true optional.get(); // "bam" optional.orElse("fallback"); // "bam" optional.ifPresent((s) -> System.out.println(s.charAt(0))); // "b" 
Потоки

Тип java.util.Stream представляет собой последовательность элементов, над которой можно производить различные операции. Операции над потоками бывают или промежуточными (intermediate) или конечными (terminal). Конечные операции возвращают результат определенного типа, а промежуточные операции возвращают тот же поток. Таким образом вы можете строить цепочки из несколько операций над одним и тем же потоком. Поток создаются на основе источников, например типов, реализующих java.util.Collection , такие как списки или множества (ассоциативные массивы не поддерживаются). Операции над потоками могут выполняться как последовательно, так и параллельно.

Сначала давайте посмотрим, как работать с потоком последовательно. Сперва создадим источник в виде списка строк:

List stringCollection = new ArrayList<>(); stringCollection.add("ddd2"); stringCollection.add("aaa2"); stringCollection.add("bbb1"); stringCollection.add("aaa1"); stringCollection.add("bbb3"); stringCollection.add("ccc"); stringCollection.add("bbb2"); stringCollection.add("ddd1"); 

В Java 8 вы можете быстро создавать потоки, используя вызовы Collection.stream() или Collection.parallelStream() . Следующие разделы объясняют наиболее распространенные операции над потоками.

Filter

Операция Filter принимает предикат, который фильтрует все элементы потока. Эта операция является промежуточной, т.е. позволяет нам вызвать другую операцию (например, forEach ) над результатом. ForEach принимает функцию, которая вызывается для каждого элемента в (уже отфильтрованном) поток. ForEach является конечной операцией. Она не возращает никакого значения, поэтому дальнейший вызов потоковых операций невозможен.

stringCollection .stream() .filter((s) -> s.startsWith("a")) .forEach(System.out::println); // "aaa2", "aaa1" 
Sorted

Операция Sorted является промежуточной операцией, которая возвращает отсортированное представление потока. Элементы сортируются в обычном порядке, если вы не предоставили свой компаратор:

stringCollection .stream() .sorted() .filter((s) -> s.startsWith("a")) .forEach(System.out::println); // "aaa1", "aaa2" 

Помните, что sorted создает всего лишь отсортированное представление и не влияет на порядок элементов в исходной коллекции. Порядок строк в stringCollection остается нетронутым:

System.out.println(stringCollection); // ddd2, aaa2, bbb1, aaa1, bbb3, ccc, bbb2, ddd1 
Map

Промежуточная операция map преобразовывает каждый элемент в другой объект при помощи переданной функции. Следующий пример преобразовывает каждую строку в строку в верхнем регистре. Однако вы так же можете использовать map для преобразования каждого объекта в объект другого типа. Тип результирующего потока зависит от типа функции, которую вы передаете при вызове map .

stringCollection .stream() .map(String::toUpperCase) .sorted((a, b) -> b.compareTo(a)) .forEach(System.out::println); // "DDD2", "DDD1", "CCC", "BBB3", "BBB2", "AAA2", "AAA1" 
Match

Для проверки, удовлетворяет ли поток заданному предикату, используются различные операции сопоставления (match). Все операции сопоставления являются конечными и возвращают результат типа boolean.

boolean anyStartsWithA = stringCollection .stream() .anyMatch((s) -> s.startsWith("a")); System.out.println(anyStartsWithA); // true boolean allStartsWithA = stringCollection .stream() .allMatch((s) -> s.startsWith("a")); System.out.println(allStartsWithA); // false boolean noneStartsWithZ = stringCollection .stream() .noneMatch((s) -> s.startsWith("z")); System.out.println(noneStartsWithZ); // true 
Count

Операция Count является конечной операцией и возвращает количество элементов в потоке. Типом возвращаемого значения является long .

long startsWithB = stringCollection .stream() .filter((s) -> s.startsWith("b")) .count(); System.out.println(startsWithB); // 3 
Reduce

Эта конечная операция производит свертку элементов потока по заданной функции. Результатом является опциональное значение.

Optional reduced = stringCollection .stream() .sorted() .reduce((s1, s2) -> s1 + "#" + s2); reduced.ifPresent(System.out::println); // "aaa1#aaa2#bbb1#bbb2#bbb3#ccc#ddd1#ddd2" 
Параллельные потоки

Как уже упоминалось выше, потоки могут быть последовательными и параллельными. Операции над последовательными потоками выполняются в одном потоке процессора, над параллельными — используя несколько потоков процессора.

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

Сперва создадим большой список из уникальных элементов:

int max = 1000000; List values = new ArrayList<>(max); for (int i = 0; i

Теперь измерим время сортировки этого списка.

Последовательная сортировка
long t0 = System.nanoTime(); long count = values.stream().sorted().count(); System.out.println(count); long t1 = System.nanoTime(); long millis = TimeUnit.NANOSECONDS.toMillis(t1 - t0); System.out.println(String.format("sequential sort took: %d ms", millis)); // sequential sort took: 899 ms 
Параллельная сортировка
long t0 = System.nanoTime(); long count = values.parallelStream().sorted().count(); System.out.println(count); long t1 = System.nanoTime(); long millis = TimeUnit.NANOSECONDS.toMillis(t1 - t0); System.out.println(String.format("parallel sort took: %d ms", millis)); // parallel sort took: 472 ms 

Как вы можете видеть, оба куска кода практически идентичны, однако параллельная сортировка почти в два раза быстрее. Все, что вам нужно сделать, это заменить вызов stream() на parallelStream() .

Ассоциативные массивы

Как уже упоминалось, ассоциативные массивы (maps) не поддерживают потоки. Вместо этого ассоциативные массивы теперь поддерживают различные полезные методы, которые решают часто встречаемые задачи.

Map map = new HashMap<>(); for (int i = 0; i < 10; i++) < map.putIfAbsent(i, "val" + i); >map.forEach((id, val) -> System.out.println(val)); 

Этот код в особых комментариях не нуждается: putIfAbsent позволяет нам не писать дополнительные проверки на null; forEach принимает потребителя, который производит операцию над каждым элементом массива.

Этот код показывает как использовать для вычислений код при помощи различных функций:

map.computeIfPresent(3, (num, val) -> val + num); map.get(3); // val33 map.computeIfPresent(9, (num, val) -> null); map.containsKey(9); // false map.computeIfAbsent(23, num -> "val" + num); map.containsKey(23); // true map.computeIfAbsent(3, num -> "bam"); map.get(3); // val33 

Затем мы узнаем, как удалить объект по ключу, только если этот объект ассоциирован с ключом:

map.remove(3, "val3"); map.get(3); // val33 map.remove(3, "val33"); map.get(3); // null 

Еще один полезный метод:

map.getOrDefault(42, "not found"); // not found 

Объединить записи двух массивов? Легко:

map.merge(9, "val9", (value, newValue) -> value.concat(newValue)); map.get(9); // val9 map.merge(9, "concat", (value, newValue) -> value.concat(newValue)); map.get(9); // val9concat 

В случае отсутствия ключа Merge создает новую пару ключ-значение. В противном случае — вызывает функцию объединения для существующего значения.

API для работы с датами

Java 8 содержит совершенно новый API для работы с датами и временем, расположенный в пакете java.time . Новый API сравним с библиотекой Joda-Time, однако имеются отличия. Следующие разделы рассказывают о наиболее важных частях нового API.

Clock

Тип Clock предоставляет доступ к текущей дате и времени. Этот тип знает о часовых поясах и может использоваться вместо вызова System.currentTimeMillis() для возвращения миллисекунд. Такая точная дата также может быть представлена классом Instant . Объекты этого класса могут быть использованы для создания объектов устаревшего типа
java.util.Date .

Clock clock = Clock.systemDefaultZone(); long millis = clock.millis(); Instant instant = clock.instant(); Date legacyDate = Date.from(instant); // legacy java.util.Date 
Часовые пояса

Часовые пояса представлены типом ZoneId . Доступ к ним можно получить при помощи статических фабричных методов. Часовые пояса содержат смещения, которые важны для конвертации дат и времени в местные.

System.out.println(ZoneId.getAvailableZoneIds()); // prints all available timezone ids ZoneId zone1 = ZoneId.of("Europe/Berlin"); ZoneId zone2 = ZoneId.of("Brazil/East"); System.out.println(zone1.getRules()); System.out.println(zone2.getRules()); // ZoneRules[currentStandardOffset=+01:00] // ZoneRules[currentStandardOffset=-03:00] 
LocalTime

Тип LocalTime представляет собой время с учетом часового пояса, например, 10pm или 17:30:15. В следующем примере создаются два местных времени для часовых поясов, определенных выше. Затем оба времени сравниваются, и вычисляется разница между ними в часах и минутах.

LocalTime now1 = LocalTime.now(zone1); LocalTime now2 = LocalTime.now(zone2); System.out.println(now1.isBefore(now2)); // false long hoursBetween = ChronoUnit.HOURS.between(now1, now2); long minutesBetween = ChronoUnit.MINUTES.between(now1, now2); System.out.println(hoursBetween); // -3 System.out.println(minutesBetween); // -239 

Тип LocalTime содержит различные фабричные методы, которые упрощают создание новых экземпляров, а также парсинг строк.

LocalTime late = LocalTime.of(23, 59, 59); System.out.println(late); // 23:59:59 DateTimeFormatter germanFormatter = DateTimeFormatter .ofLocalizedTime(FormatStyle.SHORT) .withLocale(Locale.GERMAN); LocalTime leetTime = LocalTime.parse("13:37", germanFormatter); System.out.println(leetTime); // 13:37 
LocalDate

Тип LocalDate представляет конкретную дату, например, 2014-03-11. Объекты LocalDate неизменяемы и являются аналогом LocalTime . Пример демонстрирует вычисление новой даты путем сложения или вычитания дней, месяцев или годов. Помните, что каждая операция возвращает новый экземпляр.

LocalDate today = LocalDate.now(); LocalDate tomorrow = today.plus(1, ChronoUnit.DAYS); LocalDate yesterday = tomorrow.minusDays(2); LocalDate independenceDay = LocalDate.of(2014, Month.JULY, 4); DayOfWeek dayOfWeek = independenceDay.getDayOfWeek(); System.out.println(dayOfWeek); // FRIDAY 

Создание экземпляра LocalDate путем парсинга строки:

DateTimeFormatter germanFormatter = DateTimeFormatter .ofLocalizedDate(FormatStyle.MEDIUM) .withLocale(Locale.GERMAN); LocalDate xmas = LocalDate.parse("24.12.2014", germanFormatter); System.out.println(xmas); // 2014-12-24 
LocalDateTime

Тип LocalDateTime представляет собой комбинацию даты и времени. Объекты LocalDateTime неизменяемы и работают аналогично LocalTime и LocalDate . Мы можем использовать различные методы для извлечения конкретных значений из даты-времени:

LocalDateTime sylvester = LocalDateTime.of(2014, Month.DECEMBER, 31, 23, 59, 59); DayOfWeek dayOfWeek = sylvester.getDayOfWeek(); System.out.println(dayOfWeek); // WEDNESDAY Month month = sylvester.getMonth(); System.out.println(month); // DECEMBER long minuteOfDay = sylvester.getLong(ChronoField.MINUTE_OF_DAY); System.out.println(minuteOfDay); // 1439 

Путем добавления информации о часовом поясе мы можем получить Instant .

Instant instant = sylvester .atZone(ZoneId.systemDefault()) .toInstant(); Date legacyDate = Date.from(instant); System.out.println(legacyDate); // Wed Dec 31 23:59:59 CET 2014 

Форматирование даты-времени работает так же, как и форматирование даты или времени. Мы можем использовать библиотечные или свои собственные шаблоны.

DateTimeFormatter formatter = DateTimeFormatter .ofPattern("MMM dd, yyyy - HH:mm"); LocalDateTime parsed = LocalDateTime.parse("Nov 03, 2014 - 07:13", formatter); String string = formatter.format(parsed); System.out.println(string); // Nov 03, 2014 - 07:13 

В отличии от java.text.NumberFormat , новый DateTimeFormatter является неизменяемым и потокобезопасным.

Подробно о синтаксисе шаблонов можно почитать здесь.

Аннотации

Аннотации в Java 8 являются повторяемыми. Давайте сразу посмотрим пример, чтобы понять, что это такое.

Сперва мы определим аннотацию-обертку, которая содержит массив аннотаций:

@interface Hints < Hint[] value(); >@Repeatable(Hints.class) @interface Hint

Java 8 позволяет нам использовать множество аннотаций одного типа путем указания аннотации @Repeatable .

Вариант 1: использовать аннотацию-контейнер (старый способ)

@Hints() class Person <> 

Вариант 2: использовать повторяемую аннотацию (новый способ)

@Hint("hint1") @Hint("hint2") class Person <> 

При использовании варианта 2 компилятор автоматически подставляет аннотацию @Hints . Это важно при чтении информации об аннотациях через рефлексию.

Hint hint = Person.class.getAnnotation(Hint.class); System.out.println(hint); // null Hints hints1 = Person.class.getAnnotation(Hints.class); System.out.println(hints1.value().length); // 2 Hint[] hints2 = Person.class.getAnnotationsByType(Hint.class); System.out.println(hints2.length); // 2 

Хотя мы никогда не объявляли аннотацию @Hints в классе Person , она доступна нам при вызове getAnnotation(Hints.class) . Однако более удобным является метод getAnnotationsByType , который напрямую предоставляет доступ ко всем аннотациям @Hint .

Более того, аннотации в Java 8 можно использовать еще на двух элементах:

@Target() @interface MyAnnotation <> 
Вот и все

На этом введение в программирование на Java 8 завершено. Вам остается самостоятельно изучить другие новинки JDK 1.8, например, Arrays.parallelSort , StampedLock , CompletableFuture и другие.

Полный исходный код статьи доступен на GitHub.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *