Продолжение первой части статьи, и разбор нововведений Java 8 на примерах.
Обратите внимание на первую часть Java 8 Killer Features. Часть 1.
Что мы узнаем нового в Java 8?
6. Встроенные функциональные интерфейсы
6. Встроенные функциональные интерфейсы
Java 8 принесла с собой еще несколько давно известных нам, но в тот же момент новых вещей, а именно множество встроенных функциональных интерфейсов. Да вы и раньше их не раз использовали, на пример Comparator или Runnable.
Теперь все эти интерфейсы стали функциональными интерфейсами и если посмотреть в Исходники то можно увидеть что они проаннотированы как @FunctionalInterface.
Давайте рассмотрим новые встроенные функциональные интерфейсы Java 8. Я покажу лишь перевод, так как Встроенные функциональные интерфейсы требуют отдельного внимания.
Предикаты
Предикаты — это функции, принимающие один аргумент, и возвращающие значение типа boolean.
Интерфейс содержит различные методы по умолчанию, позволяющие строить сложные условия (and, or, negate).
Predicate<String> predicate = (s) -> s.length() > 0; predicate.test("foo"); // true predicate.negate().test("foo"); // false Predicate<Boolean> nonNull = Objects::nonNull; Predicate<Boolean> isNull = Objects::isNull; Predicate<String> isEmpty = String::isEmpty; Predicate<String> isNotEmpty = isEmpty.negate();
Функции
Функции принимают один аргумент и возвращают некоторый результат. Методы по умолчанию могут использоваться для построения цепочек вызовов (compose, andThen).
Function<String, Integer> toInteger = Integer::valueOf; Function<String, String> backToString = toInteger.andThen(String::valueOf); backToString.apply("123"); // "123"
Поставщики
Suppliers – предоставляют результат заданного типа. В отличии от функций, поставщики не принимают аргументов.
Supplier<Person> personSupplier = Person::new; personSupplier.get(); // new Person
Потребители
Consumers – представляют собой операции, которые производятся на одним входным аргументом.
Consumer<Person> greeter = (p) -> System.out.println("Hello, " + p.firstName); greeter.accept(new Person("Luke", "Skywalker"));
Компараторы
Компараторы хорошо известны по предыдущим версиям Java. Java 8 добавляет в интерфейс различные методы по умолчанию.
Comparator<Person> 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 — это по контейнер для значения, которое может быть null.
Например, вам нужен метод, который возвращает какое-то значение, но иногда он должен возвращать пустое значение. Вместо того, чтобы возвращать null, в Java 8 вы можете вернуть опциональное значение.
Optionals не являются функциональными интерфейсами, однако являются удобным средством предотвращения всеми известным NullPointerException.
Optional<String> optional = Optional.of("bam"); optional.isPresent(); // true optional.get(); // "bam" optional.orElse("fallback"); // "bam" optional.ifPresent((s) -> System.out.println(s.charAt(0))); // "b"
7. Потоки
java.util.Stream – предоставляет возможность обрабатывать последовательность элементов исполняя одну или несколько операций, которые могут выполняться либо последовательно либо паралельно. Такая возможность предоставленна в реализациях Collection. Рассмотрим на примерах. Для начало создадим коллекцию, которую будем обрабатывать. Map не поддерживается.
List<String> strings = new ArrayList<>(); strings.add("Hello"); strings.add("World"); strings.add("!"); strings.add("We love"); strings.add("Java 8");
Фильтруем данные
Для того чтобы отфильтровать коллекцию нам понадобиться вызвать метод filter, который принимает предиката. Выглядит это проще чем может звучать.
strings .stream() .filter(s -> s.startsWith("W")) .forEach(System.out::println);
Здесь, мы берем поток(последовательности), фильтруем и передаем в стандартный поток вывода.
В результате получим: (World We love).
Сортировка данных
В сортировке важно помнить:
1. Сортировка производится по стандартному, если не указать свой Comparator
2. Объекты внутри коллекции не сортируются. Просто возвращается отсортированная коллекция
strings .stream() .sorted() .filter(s -> s.startsWith("W")) .forEach(System.out::println);
В результате получим: (We love World).
Map
Предназначение – конвертация в другой тип, используя функцию. Поменяем наши входные данные и попробуем строки переобразовать в числа.
strings.add("1"); strings.add("2"); strings.add("3"); strings.add("4"); strings.add("5"); strings .stream() .sorted() .map(Integer::valueOf) .forEach(System.out::println);
Как по мне, очень удобно, быстро и без малейших затрат. Идем дальше.
Match
Он поможет найти вхождения, соответствия обьекта шаблону. Для использования также используются предикаты. Есть 3 вида сравнения:
1. Все соответствуют шаблону allMatch()
2. Хоть один соответствует шаблону anyMatch()
3. Ни один не соответствует noneMatch()
//false так как не все == 1 boolean allMatch = strings .stream() .allMatch(s -> s.startsWith("1")); System.out.println(allMatch); //true так как 1 элемент == 1 boolean anyMatch = strings .stream() .anyMatch(s -> s.startsWith("1")); System.out.println(anyMatch); //true так как 9 не содержиться в коллекции boolean noneMatch = strings .stream() .noneMatch(s -> s.startsWith("9")); System.out.println(noneMatch);
Count
Возвратит нам количество элементов . Допустим, просле проведения фильтрации, нужно узнать число элементов.
//Вернет 1, у нас одна 2-ка в коллекции long count = strings .stream() .map(Long::valueOf) .filter(i -> i.equals(2L)) .count(); System.out.println(count);
8. Параллельные потоки
До этого, операции в коллекции выполнялись в одном потоке. Здесь посмотрим, как выполнить комманду в нескольких потоках. Для этого действительно достаточно вызвать parallelStream(), и продолжить работу с ним как и прежде.
List<Integer> integers = new ArrayList<>(); for (int i = 0; i < 10; i++) { integers.add((int)(Math.random()*10)); } integers .parallelStream() .filter(i -> i % 2 == 0) .sorted() .forEachOrdered(System.out::println);
В нашем примере, мы фильтруем данные, которые делятся на 2 без остатка, проводим сортировку и выводим результат. Обратите внимаение что для вывода используется forEachOrdered(), он гарантирует вывод вашей коллекции корректно.
9. Map
Хоть этот тип коллекции и не поддерживает потоки, но все же к нему было добавлено несколько важных методов.
getOrDefault() – возвращает значение, или значение по умолчание, если null
forEach() – выполнение действий с каждым элементом карты
replaceAll() – производит замену для всей коллекции по заданной функции
putIfAbsent() – добавление, если по ключу null, и возвращение значения
remove(Object key, Object value) – удаление, если ключ производит маппинг на конкретное значение
computeIfAbsent() – принимает функцию для вычисления null значения или еще не заданного
computeIfPresent() – устанавливаем значения для существуещего ключа, используя функцию
merge() – объединение наших значений
Это не все методы которые присутствуют в новой версии. Используем их на примере:
Map<Integer, String> map = new HashMap<>(); for (int i = 0; i < 10; i++) { map.putIfAbsent(i, "value_" + i); } //В коллекции в значении по ключу 2 будет "value_2Updated" map.computeIfPresent(2, (k,v) -> v + "Updated"); //Никаких изминений в коллекции не будет, так как по ключу 100 у нас нету значений map.computeIfPresent(100, (k,v) -> "New Value"); //Изминений мы также не увидим map.computeIfAbsent(1, v -> "1"); //А вот так у нас добавиться новый элемент в map map.computeIfAbsent(10, v -> "value_10"); //Удаляем элемент, с данным ключем и значением map.remove(10, "value_10"); //Теперь мы получим сообщение о том, что значение было удалено Object o = map.getOrDefault(10, "Value was deleted!"); System.out.println(o); //Обьединение значений в итоге получим "value_0_1" вместо "value_0" map.merge(0,"_1", (v,nv) -> v.concat(nv)); //Добавит ко всем значением подчеркивание вначале map.replaceAll((k,v) -> v="_" + v); map.forEach((id, val) -> System.out.println(val));
10. Date API
Много нововведений нас ждет также и в управлении временем и датой. Все они находятся в пакете java.time. Множество с классов внутри являются immutable.
Основные пакеты:
java.time – отвечает за работу со временем и датой. Классы есть потокобезопасными и не изменяемые.
java.time.chrono – для предоставления времени в другом стандарте чем стандартный(ISO-8601)
java.time.format – для форматирования и парсинга даты
java.time.temporal – расширенное API для фреймворков и разработчиков библиотек.
java.time.zone – работа с временными зонами
Рассмотрим примеры работы и новые возможности:
LocalDate
LocalDate date = LocalDate.now(); /*Проводятся операции по смещению времени в сторону увеличения*/ date = date .plusYears(1) .plusMonths(1) .plusWeeks(2) .plusDays(5); /*Проводим смещение времени в сторону уменьшения*/ date = date .minusYears(1) .minusMonths(1) .minusWeeks(2) .minusDays(5); /*Форматирование вывода*/ date .format(DateTimeFormatter.BASIC_ISO_DATE); System.out.println(date); /*Задаем дату используя строку*/ LocalDate dateFromString = LocalDate.parse("2014-04-16"); System.out.println(dateFromString);
Clock
Содержит в себе текущее время и дату в зависимости от локали. Можно получить время в миллисекундах, а также инстанс даты.
Clock clock = Clock.systemDefaultZone(); Date date = Date.from(clock.instant()); long millis = clock.millis(); System.out.println(date);
Timezones
Доступ к временным зонам представлен через специальный идентификатор и фабрику по созданию.
ZoneId zoneId1 = ZoneId.of("Europe/Paris"); ZoneId zoneId2 = ZoneId.of("Brazil/East"); System.out.println(zoneId1.getRules()); System.out.println(zoneId2.getRules());
Результатом будет:
ZoneRules[currentStandardOffset=+01:00]
ZoneRules[currentStandardOffset=-03:00]
DateTimeFormatter
Бывает что дату мы получаем в определенном формате и нужно ее как-то обработать. Для этого есть очень удобный способ:
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd.MM.yyyy - HH:mm"); LocalDateTime dateTime = LocalDateTime.parse("21.03.2014 - 12:00", formatter); String formatted = formatter.format(dateTime); System.out.println(formatted);
Результатом работы программы будет получение даты за заданным шаблоном, а потом преобразовании полученной даты в этот же формат:
21.03.2014 – 12:00
Более детально, о паттернах на офф. сайте.
11. Аннотации
Использование множества аннотаций одного типа. Допустим, У нас есть класс, который нужно аннотировать аннотацией Annotation дважды, просто с разными параметрами. Раньше мы б писали вот так:
public @interface Annotation { String value(); } @interface Annotations{ Annotation[] value(); } //Аннотированный класс @Annotations({@Annotation("1"),@Annotation("2")}) public class Book { }
Теперь используя аннотацию Repeatable мы можем написать это в более читабельной форме:
@Repeatable(Annotations.class) public @interface Annotation { String value(); } @interface Annotations{ Annotation[] value(); } //и наш класс @Annotation("1") @Annotation("2") public class Book { }
Помимо этого есть еще одно обновление. Оно относится к новым типам target’ов в аннотациях.
@Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE}) @interface Annotation {}
ElementType.TYPE_PARAMETER – параметризованный тип(MyClass<T>)
TYPE_USE – принимает любой тип параметра
На этом думаю пора бы и закончить. Для написания данного урока использовали: winterbe.com, а также просторы документаций Oracle.
ПОХОЖИЕ ПУБЛИКАЦИИ
- None Found
5 комментариев к статье "Java 8 Killer Features. Часть 2"
Добавить комментарий
Для отправки комментария вам необходимо авторизоваться.
Спасибо за пост.
В примере кода про предикаты ошибка:
должно быть
Predicate nonNull = Objects::nonNull;
Predicate isNull = Objects::isNull;
Predicate\ nonNull = Objects::nonNull;
Predicate\ isNull = Objects::isNull;
Здравствуй Александр.
9. Map
Хоть этот тип коллекции и не поддерживает потоки …
Карта – это не коллекция, они не состоят в родстве. Карта вообще стоит обособленно.
А так статья познавательная, спасибо.
Он просто ошибочно указал map с большой буквы. Это новая функция в java 8 – mapping – преобразование одного типа в другой. Никаго отношения к семеству коллекций Map эти фунции не имеют (именно функции, так как их там несколько, например, mapToInt)