Java 8 была выпущена в начале 2014 года. В этом руководстве перечислены важные функции Java 8 с примерами, такими как лямбда-выражения, потоки Java, функциональные интерфейсы, методы по умолчанию и изменения API даты и времени.
- Лямбда-выражения
- Функциональные Интерфейсы
- Методы по умолчанию
- Потоки Java 8
- Изменения API даты и времени Java 8
5.1. Классы Date
5.2. Временные метки и Duration классы
Лямбда-выражения
Лямбда-выражения известны многим из нас, кто работал над другими популярными языками программирования, такими как Scala. На языке программирования Java лямбда-выражение (или функция) — это просто анонимная функция, то есть функция без имени и без привязки к идентификатору.
Лямбда-выражения записываются именно там, где это необходимо, обычно в качестве параметра какой-либо другой функции.
Несколько основных видов синтаксиса лямбда-выражений следующие:
(parameters) -> expression
(parameters) -> { statements; }
() -> expression
Типичный пример лямбда-выражения будет выглядеть следующим образом:
//Эта функция принимает два параметра и возвращает их сумму
sum (x, y) -> x + y
Обратите внимание, что в зависимости от типа x и y мы можем использовать этот метод в нескольких местах. Параметры также могут соответствовать значению int, или Integer, или просто String. В зависимости от контекста он либо добавит два целых числа, либо объединит две строки.
Правила написания лямбда-выражений:
- Лямбда-выражение может иметь ноль, один или несколько параметров.
- Тип параметров может быть явно объявлен или может быть выведен из контекста.
- Несколько параметров заключены в обязательные круглые скобки и разделены запятыми. Пустые круглые скобки используются для представления пустого набора параметров.
- При наличии единственного параметра, если выводится его тип, использование круглых скобок необязательно.
- Тело лямбда-выражений может содержать ноль, один или несколько операторов.
- Если тело лямбда-выражения содержит один оператор, фигурные скобки не являются обязательными, а возвращаемый тип анонимной функции такой же, как и у основного выражения. Если в теле есть более одного оператора, то они должны быть заключены в фигурные скобки.
Функциональные Интерфейсы
Функциональные интерфейсы также называются интерфейсами с одним абстрактным методом (SAM Interfaces). Как следует из названия, функциональный интерфейс допускает в нем ровно один абстрактный метод.
Java 8 вводит аннотацию @FunctionalInterface, которую мы можем использовать для выдачи ошибок во время компиляции, если функциональный интерфейс нарушает контракты.
Пример функционального интерфейса
//Optional annotation
@FunctionalInterface
public interface MyFirstFunctionalInterface {
public void firstWork();
}
Пожалуйста, обратите внимание, что функциональный интерфейс действителен, даже если аннотация @FunctionalInterface опущена. Аннотация нужна только для информирования компилятора о необходимости применения одного абстрактного метода внутри интерфейса.
Кроме того, поскольку методы по умолчанию не являются абстрактными, мы можем добавлять методы по умолчанию в функциональный интерфейс столько, сколько нам нужно.
Еще один важный момент, который следует помнить, заключается в том, что если функциональный интерфейс переопределяет один из общедоступных методов java.lang.Object, который также не учитывается в подсчете абстрактных методов интерфейса, поскольку любая реализация интерфейса будет иметь реализацию из java.lang.Object или в другом месте.
Например, ниже приведен вполне допустимый функциональный интерфейс.
@FunctionalInterface
public interface MyFirstFunctionalInterface
{
public void firstWork();
//Overridden from Object class
@Override
public String toString();
//Overridden from Object class
@Override
public boolean equals(Object obj);
}
Методы по умолчанию
Java 8 позволяет нам добавлять неабстрактные методы в интерфейсы. Эти методы должны быть объявлены методами по умолчанию. Методы по умолчанию были введены в java 8, чтобы включить функциональность лямбда-выражения.
Методы по умолчанию позволяют нам внедрять новые функциональные возможности в интерфейсы наших библиотек и обеспечивать двоичную совместимость с кодом, написанным для более старых версий этих интерфейсов.
Давайте разберемся на примере:
public interface Moveable {
default void move(){
System.out.println("I am moving");
}
}
Moveable интерфейс определяет метод move() и также предоставляет реализацию по умолчанию. Если какой-либо класс реализует этот интерфейс, то ему не нужно реализовывать свою собственную версию метода move(). Он может напрямую вызывать instance.move(). например
public class Animal implements Moveable{
public static void main(String[] args){
Animal tiger = new Animal();
tiger.move();
}
}
Output: I am moving
Если класс добровольно хочет настроить поведение метода move(), то он может предоставить свою собственную пользовательскую реализацию и переопределить метод.
Потоки Java 8
Еще одно существенное изменение внесло Java 8 Streams API, который предоставляет механизм для обработки набора данных различными способами, которые могут включать фильтрацию, преобразование или любой другой способ, который может быть полезен для приложения.
Streams API в Java 8 поддерживает другой тип итерации, где мы определяем набор элементов, подлежащих обработке, операции, которые должны выполняться над каждым элементом, и где должны храниться выходные данные этих операций.
В этом примере items представляет собой набор строковых значений, и мы хотим удалить записи, начинающиеся с некоторого текста с префиксом.
List<String> items = ...; //Initialize the list
String prefix = "str";
List<String> filteredList = items.stream()
.filter(e -> (!e.startsWith(prefix)))
.collect(Collectors.toList());
Здесь items.stream() указывает, что мы хотим обрабатывать данные с использованием Streams API.
Изменения API даты и времени Java 8
Новые API-интерфейсы/ классы даты и времени (JSR-310), также называемые Three Men, просто изменили способ обработки дат в приложениях Java.
Классы Date
Класс Date даже устарел. Новыми классами, предназначенными для замены класса Date, являются LocalDate, LocalTime и LocalDateTime.
- Класс LocalDate представляет дату. Нет никакой информации о времени или часовом поясе.
- Класс LocalTime представляет время. Нет никакой информации о дате или часовом поясе.
- Класс LocalDateTime представляет дату и время. Нет никакой информации о часовом поясе.
Если мы хотим использовать функциональность данных с информацией о часовом поясе, то Lambda предоставляет нам дополнительные три класса, аналогичные приведенным выше, т.е. OffsetDate, OffsetTime и OffsetDateTime.
Смещение часового пояса может быть представлено в форматах “+05:30” или “Europe/Paris”. Это делается с помощью другого класса, то есть ZoneId.
LocalDate localDate = LocalDate.now();
LocalTime localTime = LocalTime.of(12, 20);
LocalDateTime localDateTime = LocalDateTime.now();
OffsetDateTime offsetDateTime = OffsetDateTime.now();
ZonedDateTime zonedDateTime = ZonedDateTime.now(ZoneId.of("Europe/Paris"));
Временные метки и Duration классы
Для представления конкретной временной метки в любой момент необходимо использовать класс Instant. Класс Instant представляет момент времени с точностью до наносекунд.
Операции с Instant включают сравнение с другим Instant и добавление или вычитание длительности.
Instant instant = Instant.now();
Instant instant1 = instant.plus(Duration.ofMillis(5000));
Instant instant2 = instant.minus(Duration.ofMillis(5000));
Instant instant3 = instant.minusSeconds(10);
Класс Duration — это совершенно новая концепция, впервые представленная на языке java. Он представляет собой разницу во времени между двумя временными метками.
Duration duration = Duration.ofMillis(5000);
duration = Duration.ofSeconds(60);
duration = Duration.ofMinutes(10);
Duration имеет дело с небольшой единицей времени, такой как миллисекунды, секунды, минуты и часы. Они больше подходят для взаимодействия с кодом приложения.
Чтобы взаимодействовать с людьми, нам нужно получить большую продолжительность времени, представленную классом Period.
Period period = Period.ofDays(6);
period = Period.ofMonths(6);
period = Period.between(LocalDate.now(), LocalDate.now().plusDays(60));