Статьи

Java — дальний взгляд на JDK 8

Мир меняется медленно, но верно. После изменений, которые дали java более свежий взгляд с JDK 7, сообщество java с нетерпением ждет остальных улучшений, которые появятся в JDK 8 и, возможно, JDK 9.
Целевой целью JDK 8 было заполнить пробелы в реализации JDK 7 — часть оставшихся частей головоломки, вытекающих из этой реализации, которая должна быть доступна для широкой аудитории в конце 2013 года, — улучшить и расширить язык в три конкретных направления:

  • производительность
  • производительность
  • модульность

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

производительность

Что касается производительности, JDK 8 ориентирован на две основные области: — коллекции — более удобный способ взаимодействия с коллекциями посредством литеральных расширений, введенных в язык — аннотации — расширенная поддержка аннотаций, позволяющая записывать их в контекстах, где в настоящее время недопустимы (например, примитивы)

Производительность

Добавление платформы Fork / Join к JDK 7 стало первым шагом, который сделал java в направлении многоядерных процессоров. JDK 8 идет по этому пути еще дальше, предоставляя поддержку замыканий в java (то есть лямбда-выражение). Вероятно, наиболее затронутой частью java будет часть Collections, замыкания в сочетании с недавно добавленными интерфейсами и функциональными возможностями, поднимающими контейнеры java на следующий уровень. Помимо более удобочитаемого и более короткого кода, который нужно написать, предоставляя коллекциям лямбда-выражение, которое будет выполняться внутри платформы, платформа может использовать преимущества многоядерных процессоров.

модульность

Одним из наиболее интересных элементов для сообщества была головоломка проекта: «Цель этого проекта — разработать и внедрить стандартную модульную систему для платформы Java SE и применить эту систему к самой платформе и к JDK». Я использую прошедшее время, потому что для тех из нас, кто надеялся избавиться от путей к классам и загрузчиков классов, мы должны отложить нашу экспансию для Java 9, так как на тот момент проект был также отложен .

Чтобы получить более четкое представление о том, как работает Java Roadmap 2013:

  • 2013/01/31 Функция M6 завершена
  • 2013/02/21 M7 Developer Preview
  • 2013/07/05 Кандидат в финальный релиз M8
  • 2013/09/09 GA Общая доступность

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

Лямбда

Начиная

Прежде всего, нужно получить SDK с поддержкой лямбды. В этом направлении есть два способа получить один:

* тот, который предназначен для смелых: построить его из источников

* удобный: загрузка уже скомпилированной версии SDK

Первоначально я начал с его сборки из исходников, но из-за нехватки времени и слишком большого количества предупреждений, связанных с переменными среды, я выбрал ленивый подход и взял уже существующий JDK. Другим важным инструментом является текстовый редактор для написания кода. Как это и происходило до сих пор, первым делом вышел релиз JDK, а через некоторое время вышла включенная IDE. На этот раз все по-другому, возможно, также из-за прозрачности и широкой доступности SDK через openjdk. Несколько дней назад JetBrain выпустил первый IDE с поддержкой Java 8. Таким образом, IntelliJ IDEA версии 12 является первой IDE, обеспечивающей поддержку JDK 8, и есть ли улучшения? Поэтому для тестирования я использовал IntelliJ 12 Community Edition вместе с JDK 8 b68 на компьютере с Windows 7 x64. Для тех из вас, кто предпочитает Netbeans, доступна для скачивания ночная сборка с поддержкой лямбды.

Приспосабливаясь к соответствующему мышлению.

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

    • Что такое лямбда-выражение? Самый простой способ увидеть лямбда-выражение — это как метод: он предоставляет список формальных параметров и тело — выражение или блок, выраженные в терминах этих параметров. Параметры лямбда-выражения могут быть объявлены или выведены если формальные параметры имеют предполагаемые типы, то эти типы являются производными от типа функционального интерфейса, на который нацелено лямбда-выражение. С точки зрения возвращаемого значения, лямбда-выражение может быть void-совместимым — они не возвращают ничего или совместимы со значениями — если какой-либо заданный путь выполнения возвращает значение.
      Примеры лямбда-выражений:

      01
      02
      03
      04
      05
      06
      07
      08
      09
      10
      11
      (a) (int a, int b) -> a + b
       
      (b) (int a, int b) -> {
       if (a > b) {
           return a;
       } else if (a == b) {
           return a * b;
       } else {
           return b;
       }
         }
    • Что такое функциональный интерфейс? Функциональный интерфейс — это интерфейс, который содержит только один абстрактный метод и, следовательно, представляет собой контракт с одним методом. В некоторых ситуациях один метод может иметь форму нескольких методов с эквивалентными переопределенными сигнатурами, в этом случае все методы представляют один метод. Помимо типичного способа создания экземпляра интерфейса путем создания и создания экземпляра класса, экземпляры функционального интерфейса могут быть созданы также с использованием лямбда-выражений, ссылок на методы или конструкторы.
      Пример функциональных интерфейсов:

      1
      2
      3
      4
      // custom built functional interface
      public interface FuncInterface {
         public void invoke(String s1, String s2);
      }

      Пример функциональных интерфейсов из API JAVA:

      1
      2
      3
      4
      java.lang.Comparable
      java.lang.Runnable
      java.util.concurrent.Callable
      java.awt.event.ActionListener

      Итак, давайте посмотрим, как начало потока может измениться в будущем:
      СТАРЫЙ ПУТЬ:

      1
      2
      3
      4
      5
      6
      7
      8
      new Thread(new Runnable() {
               @Override
               public void run() {
                   for (int i=0; i< 9; i++) {
                       System.out.println(String.format('Message #%d from inside the thread!', i));
                   }
               }
           }).start();

      НОВЫЙ ПУТЬ:

      1
      2
      3
      4
      5
      new Thread(() -> {
           for (int i=0; i< 9; i++) {
              System.out.println(String.format('Message #%d from inside the thread!', i));
           }
       }).start();

      Даже если в течение некоторого времени я не писал никаких функций, связанных с Java, Swing, AWT, я должен признать, что лямбды дадут глоток свежего воздуха для добавления слушателя действий разработчиков Swing:

      01
      02
      03
      04
      05
      06
      07
      08
      09
      10
      11
      12
      13
      14
      JButton button = new JButton('Click');
       
      // NEW WAY:
      button.addActionListener( (e) -> {
          System.out.println('The button was clicked!');
      });
       
      // OLD WAY:
      button.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    System.out.println('The button was clicked using old fashion code!');
                }
            });
    • Кто / что такое SAM? SAM расшифровывается как Single Abstract Method, поэтому, чтобы сократить некоторые углы, мы можем сказать, что SAM == функциональный интерфейс. Даже если в исходной спецификации также абстрактные классы только с одним абстрактным методом считались типами SAM, некоторые люди находили / догадывались и о причине.
    • Ссылка на метод / конструктор

Лямбды звучат все красиво и все? Но почему-то необходимость в функциональном интерфейсе в некоторой степени ограничивает — означает ли это, что я могу использовать только интерфейсы, которые содержат один абстрактный метод? Не совсем — JDK 8 предоставляет механизм псевдонимов, который позволяет «извлекать» методы из классов или объектов. Это можно сделать с помощью недавно добавленного оператора ::. Может применяться на классах — для извлечения статических методов или на объектах для извлечения методов. Этот же оператор может быть использован и для конструкторов.

Реферирование:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
interface ConstructorReference
 
                          {
    T constructor();
}
 
interface  MethodReference {
    void anotherMethod(String input);
}
 
public class ConstructorClass {
    String value;
 
    public ConstructorClass() {
        value = 'default';
    }
 
    public static void method(String input) {
        System.out.println(input);
    }
 
    public void nextMethod(String input) {
        // operations
    }
 
    public static void main(String... args) {
        // constructor reference
        ConstructorReference
 
                           reference = ConstructorClass::new;
        ConstructorClass cc = reference.constructor();
 
        // static method reference
        MethodReference mr = cc::method;
 
        // object method reference
        MethodReference mr2 = cc::nextMethod;
 
        System.out.println(cc.value);
    }
}
  • Методы по умолчанию в интерфейсах

Это означает, что начиная с версии 8, интерфейсы java могут содержать тела методов, поэтому, проще говоря, java будет поддерживать множественное наследование без головной боли, которая обычно сопровождает его. Кроме того, предоставляя реализации по умолчанию для методов интерфейса, можно гарантировать, что добавление нового метода не создаст хаос в реализующих классах. JDK 8 добавил методы по умолчанию к интерфейсам, таким как java.util.Collection или java.util.Iterator, и благодаря этому предоставил механизм, позволяющий лучше использовать лямбда-выражения там, где это действительно необходимо.

Добавлены заметные интерфейсы:

1
2
java.util.stream.Streamable
java.util.stream.Stream

Улучшено взаимодействие коллекций

На мой взгляд, все изменения, которые идут с лямбда-проектом, являются отличным дополнением к языку, который приведёт его в соответствие с текущими стандартами и сделает его более простым и изящным, но, вероятно, это изменение, которое окажет наибольшее влияние на производительность и будет самым крутым + Вау-эффект, безусловно, является обновлением структуры коллекций. Нет, для Collection 2 нет фреймворка, нам все еще предстоит справиться со стиранием типов, но java сделает еще один важный сдвиг: от внешней к внутренней итерации. Тем самым он предоставляет разработчику механизм для элегантной фильтрации и агрегирования коллекций, а также для повышения эффективности. Предоставляя лямбда-выражение, которое будет выполняться внутри, можно использовать многоядерные процессоры на полную мощность. Давайте рассмотрим следующие сценарии:

а. Рассматривая список строк, выберите все из них, которые написаны в верхнем регистре. Как бы это было написано?
СТАРЫЙ ПУТЬ:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
//.....
  
 List inputList = new LinkedList<>();
 List upper = new LinkedList<>();
 
 // add elements
 
 for (String currentValue : inputList) {
   if (currentValue != null && currentValue.matches("[A-Z0-9]*")) {
       upper.add(currentValue);
   }
 }
 
 System.out.println(upper);

//….. НОВЫЙ ПУТЬ:

1
2
//.....
inputList.stream().filter(x -> (x != null && x.matches('[A-Z0-9]*'))).into(upper);

б. Учтите, что вы хотите изменить все извлеченные символы на строчные. Используя способ JDK8, это будет выглядеть так:

1
2
// .....
  inputList.stream().filter(x -> (x != null && x.matches("[A-Z0-9]*"))).map(String::toLowerCase).into(upper);

с. А как насчет определения количества символов из выбранной коллекции?

1
2
3
// ..... 
   
 int sumX = inputList.stream().filter(x -> (x != null && x.matches("[A-Z0-9]*"))).map(String::length).reduce(0, Integer::sum);

Используемые методы:

1
2
3
4
5
6
7
default Stream
 
                         stream() // java.util.Collection
Stream
 
                          filter(Predicate predicate) // java.util.stream.Stream
IntStream map(IntFunction mapper) //java.util.stream.Stream

д. Что если я хотел бы взять каждый элемент из коллекции и распечатать его?

1
2
3
4
5
6
7
//OLD WAY:
for (String current : list) {
  System.out.println(current);
}
  
 //NEW WAY:
 list.forEach(x -> System.out.println(x));

Помимо упомянутой функциональности, у JDK 8 есть и другие интересные новости, но для краткости я на этом остановлюсь. Более подробную информацию об этом можно найти на лямбда- сайте проекта JDK 8 или на веб-странице JSR 337.

В заключение следует сказать, что Java движется вперед, и мне лично нравится направление, в котором она движется, еще один интересный момент — это момент времени, когда разработчики библиотек тоже начнут внедрять JDK 8. Это будет наверняка интересно. Спасибо за ваше время и терпение, я желаю вам счастливого Рождества.

Ресурсы

Папка ресурсов Брайана Гетца:
http://cr.openjdk.java.net/~briangoetz/lambda

Ссылки на метод / конструктор:
http://doanduyhai.wordpress.com/2012/07/14/java-8-lambda-in-details-part-iii-method-and-constructor-referencing

Ссылка: Java — взгляд из JDK 8 от нашего партнера по JCG Olimpiu Pop в блоге Java Advent Calendar .