Выпуск Java8 запланирован на март 2014 года, но ранние версии были доступны некоторое время. Некоторые из самых интересных новых функций:
- Streams
- Функциональные интерфейсы
- Методы по умолчанию
- Лямбда
- Время Java
Streams
Новый пакет java.util.stream содержит «классы для поддержки операций функционального стиля над потоками элементов». Потоки не являются новым типом коллекции и не заменяют ни одну из существующих, таких как списки и очереди. Вместо этого они предоставляют способ взаимодействия с существующей коллекцией и в этом отношении больше похожи на итераторы .
Javadoc описывает поток как «последовательность элементов, поддерживающих последовательные и параллельные операции агрегирования». Потоковый конвейер состоит из источника (например, коллекции), промежуточных операций (например, фильтра или карты) и операции терминала, которая выдает результат (например, сумму или счет). Потоки ленивы из-за того, что операции с данными выполняются только в последнюю минуту, т.е. когда вызывается операция терминала, а поток обрабатывается только один раз.
Например:
1
2
3
4
|
int totalFxTrading = blocks.stream() .filter(trade -> trade.getType() == FX) .mapToInt(b -> b.getTradedAmount()) .sum(); |
Функциональные интерфейсы
Java 8 будет иметь новую функцию под названием функциональные интерфейсы. Функциональный интерфейс имеет ровно один абстрактный метод. Существует множество таких интерфейсов, которые вы, вероятно, использовали в качестве разработчика Java, таких как Runnable, ActionListener, Comparator и Callable. В Java 8 эти типы интерфейсов теперь более формально называют функциональными интерфейсами. Их можно идентифицировать с помощью новой аннотации @FunctionalInterface и, что наиболее важно, можно представить с помощью лямбда-выражений (более подробно). Например, чтобы использовать ActionListener в прошлом, вам нужно было создать реализацию, часто используя анонимный внутренний класс.
Например:
1
2
3
4
5
6
7
|
JButton button = new JButton(); button.addActionListener( new ActionListener() { @Override public void actionPerformed(ActionEvent ae) { System.out.println(ae.getSource()); } }); |
Используя функциональные интерфейсы, это становится намного проще:
1
2
3
4
|
JButton myButton = new JButton(); button.addActionListener( ae -> {System.out.println( "You clicked the button" );} ); |
Нам даже не нужно упоминать ActionEvent — компилятор получает тип «ae» из контекста. Обратите внимание, что аннотация @FunctionalInterface , как и аннотация @Override , не требуется. Вместо этого он сообщает компилятору о ваших намерениях, чтобы он мог предупредить вас, если что-то выглядит не так, например, доступно более одного абстрактного метода.
Методы по умолчанию
До Java7 интерфейс был довольно простой вещью. Он может содержать только абстрактные методы (и константы), которые должны быть реализованы конкретными подклассами. Интерфейс был в основном набором сигнатур методов, но никогда не мог содержать определение / реализацию метода.
В Java8 все становится интереснее. Методы по умолчанию теперь могут быть добавлены в интерфейс. Это методы, у которых есть реализация, которые не нужно переопределять в реализации интерфейса, и их можно запускать непосредственно из интерфейса.
Эти методы по умолчанию были добавлены как необходимость обеспечения обратной совместимости. Если бы они не были добавлены, было бы невозможно расширить / улучшить существующие интерфейсы коллекции, например, без нарушения всех реализаций. По этой причине методы по умолчанию иногда называют методами защитника.
Для меня действительно интересной особенностью методов по умолчанию является то, что они допускают форму множественного наследования. Поскольку класс может реализовывать более одного интерфейса, и каждый из этих интерфейсов теперь может иметь метод по умолчанию с тем же именем, какую версию наследует подкласс? Я думаю, что это называется проблемой алмазов . Если такой сценарий возникает при использовании Java8, компилятор выдаст предупреждение. Вы можете использовать синтаксис X.super.m (…), чтобы явно выбрать одну из реализаций родительского класса.
Кстати, зачем этим новым методам по умолчанию вообще нужно ключевое слово по умолчанию? Разве они не могли бы работать так же хорошо без этого ключевого слова? Ответ — да, ключевое слово по умолчанию является избыточным, как и абстрактное ключевое слово. Оба были добавлены, чтобы сделать вещи немного более ясными. В этом посте есть еще какие-то подробности ссылки.
лямбда
Согласно Википедии , лямбда-выражение — это «функция, определенная без привязки к идентификатору». Лямбда-выражения поступают в Java в версии, предназначенной для упрощения кода.
Многие другие изменения, которые я обсуждал выше (методы по умолчанию, функциональные интерфейсы), очень тесно связаны с введением lambas.
Когда написано лямбда-выражение, оно преобразуется в функциональный интерфейс во время компиляции. Вот пример использования лямбда-выражений для замены анонимного внутреннего класса на более чистый и читаемый код.
Старый путь без лямбды:
1
2
3
4
5
|
button.addActionListener( new ActionListener() { public void actionPerformed(ActionEvent ae) { System.out.println(“Action Detected”); } }); |
Новый способ с Lambda:
1
2
3
|
button.addActionListener(e -> { System.out.println(“Action Detected”); }); |
Использование лямбда-выражений часто облегчает чтение кода и требует меньше строк.
Время Java
Работа с датами и временем на Java никогда не была идеальной. В лучшем случае это было странно, а в худшем — немного кошмарно. Класс Date был неуклюжим и теперь завален устаревшими методами. Класс Calendar был улучшением, но я лично, кажется, всегда трачу больше времени, которое мне бы хотелось, чтобы пролистать документы API, даже для вещей, которые я делал раньше. Другие сторонние библиотеки пытались справляться со временем более элегантно (например, время Joda). Ходили слухи об улучшенной обработке в Java, как и в Java8, именно здесь — пакет java.time .
Новый (абстрактный) класс Clock предоставляет некоторые полезные статические методы, такие как systemUTC () и systemDefaultZone () для получения текущего времени и часового пояса. А ряд новых классов, таких как LocalDate и LocalTime, а также YearMonth и MonthDay обеспечивают более элегантную обработку повседневных (простите за каламбур) операций с датами.
Более подробную информацию можно найти на сайте JSR-310 .
Что не в Java8
Напомним, что хотя обработка даты и времени будет улучшена в Java8, удивительно, что до сих пор нет хорошего способа обработки валюты в Java (то, о чем я писал в прошлом). Двойные числа (и все числа с плавающей запятой) по своей сути не подходят для денег, или где-либо точные вычисления не требуются Использование int или long требует отслеживания десятичных точек, и BigDecimal тоже не идеален. JSR-354 может улучшить ситуацию, но, похоже, нам придется подождать до Java 9 для этого.