суббота, 7 января 2012 г.

Будущее Java – грядущие новшества Java 8

По результатам выступлений на конференции "Сиклум Java Субботник" и Встречи JUG KPI, несколько небольших примеров и пояснений к презентации "Будущее Java, грядущие новшества Java 8" (видео доклада), а также исходный код приводимых примеров. Для начала стоит предупредить, что все излагаемое в презентации может измениться; не думаю, правда, что радикально, но все же. Так, к примеру, за время, прошедшее между двумя докладами, defender methods успели окончательно сменить название на virtual extension methods. И второе, те примеры кода, которые приводились во время презентации, работают! Точнее, почти работают... Вы можете скачать предварительную версию JDK 8 с поддержкой лямбда-выражений и начать экспериментировать.
  • Virtual extension methods
Итак, предположим, мы имеем интерфейс NewInterface и хотим в него добавить новый метод void test() без необходимости его реализации в классах, которые реализуют данный интерфейс. Для этого в третьей строке, после описания метода, добавляем ключевое слово default и указываем имя класса, а также статического метода в данном классе, который будет вызываться в случае обращения к методу void test().
interface NewInterface{
void test2();
void test() default DefaultClass.test;
          //default { DefaultClass.test(this);};
}
Код в третьей строке и будет тем самым virtual extension method. В одном из описаний упоминается, что к методу вызываемому по умолчанию, после слова default можно обращаться так, как сделано в 4 строке, но в данный момент такой код не компилируется. Класс DefaultClass выглядит следующим образом:
class DefaultClass {
    public static void test(NewInterface ni){
        System.out.println("Default Hello");
    }
}
Теперь можно написать класс NewClass реализующий наш интерфейс NewInterface:
class NewClass implements NewInterface{
 public void test2(){
  System.out.println("My Hello");
 }
}
Метод test2() "нормальный", поэтому его мы обязаны реализовать. Если мы реализуем и метод void test(), то при обращении к нему будет вызываться данная реализации, иначе - дефолтная реализация из класса DefaultClass. Протестируем то, что получилось, выполнив следующий код:
public class Java8DefenderTest {
    public static void main(String[] args) {
        NewInterface ni = new NewClass();
  ni.test2();
  ni.test();
 }
}
Компиляция проходит без ошибок, а вот при запуске получаем следующее: My Hello Exception in thread "main" java.lang.AbstractMethodError: java8defender.NewClass.test()V at java8defender.Java8DefenderTest.main(Java8DefenderTest.java:26) И с этим, к сожалению, я пока не знаю что делать :( Можно было бы предположить, что поддержка virtual extension methods еще не реализована, но новые методы по работе с коллекциями их уже успешно используют. С тем, как с помощью virtual extension methods реализовать множественное наследование поведения, вы можете ознакомиться на слайде 11 презентации. Дополнительную информацию о virtual extension methods можно почерпнуть из документа "Interface evolution via virtual extension methods", а для любителей дискретной математики и формальных грамматик из "Featherweight Defenders: A formal model for virtual extension methods in Java".
  • Лямбда-выражения
Говоря короток, лямбда-выражение - это удобный способ реализации анонимного класса на основе интерфейса, содержащего единственный метод. Интерфейс содержащий единственный метод называется функциональным интерфейсом (functional interface). К примеру, такие интерфейсы как Runnable, Callable, EventHandler и Comparator являются функциональными. Рассмотрим пример. Пусть у нас есть следующий интерфейс:
interface F1{
 int add(int x, int y);
 }
Его реализация с помощью анонимного класса и вызов метода add(int x, int y) будут следующими:
F1 fun = new F1(){
 public int add(int x, int y){
  return x+y;
 }
};
fun.add(3,5);
С помощью лямбда-выражения данный анонимный класс переписывается следующим образом:
F1 func1 = (a,b) -> a+b;
int i = 5, j = 13;
func1.add(i,j);
В круглых скобках указываются входные параметры (в соответствии с параметрами метода), а после стрелки - тело (реализация) того самого единственного метода, который был объявлен в интерфейсе. При желании его (тело) можно заключить в фигурные скобки и написать return:
F1 func1 = (a,b) -> {
        System.out.println("a=" + a);
        System.out.println("b=" + b);
        return a+b;
}
int i = 5, j = 13;
func1.add(i,j);
В блоке кода может быть реализована и любая другая функциональность, а также могут выполняться обращения к переменным за пределами лямбда-выражения. Ограничение: внешние переменные должны быть т.н. effectively final, то есть вести себя как переменная перед которой стоял бы модификатор final. Основное значение и преимущество лямбда-выражений состоит в том, что их можно передавать в качестве параметров в методы в удобном компактном виде (пример на слайде 26). Так же планируется, что в будущем можно будет в качестве параметров передавать ссылки на методы и конструкторы, без их непосредственного вызова. К примеру:
String::isEmpty
"abc"::length
Person::getLastName
ArrayList::new

Но на данный момент эта функциональность еще не работает. Детальнее про лямбда-выражения можно почитать в проекте Lambda Specification либо в более короткой статье State of the Lambda (кстати обнаружил ее новую версию лишь сейчас).
  • Functional Collection Patterns
Судя по тому, что написано в JDK Enhancement Proposals (JEP), virtual extension methods и лямбда-выражения добавляются в Java с целью введение функциональных методов (функций высшего порядка) по работе с коллекциями - Functional Collection Patterns. Эти методы: filter, map, reduce (свертка), а так же целый ряд других, уже добавлены в интерфейс Iterable.java с использованием virtual extension methods. Данные методы будут работать с коллекциями и в качестве параметров будут принимать лямбда-выражения, которые и будут определять, чего именно мы хотим добиться от нашей коллекции:
List students = ...
double highestScore =
    students.filter(s -> s.getGradYear() == 2010 || s.getGradYear() == 2011)
        .map(s -> s.getScore())
   //.map(Student::getScore) //либо указатель на функцию, пока не работает
     .reduce(0.0, (left, right) -> (left > right) ? left : right );
Но основной целью является даже не добавление методов по работе с коллекциями, а добавление параллельных версий этих методов, оптимизированных для оптимальной работы с большими объемами данных на многоядерных (многопроцессорных) системах. Параллелизм будет основываться на подходе «divide-and-conquer» с использование Fork-Join framework из Java 7. Загляните в исходный код ParallelIterables.java и увидите там универсальный алгоритм для параллельной работы с коллекциями. К сожалению, статей и материалов по данной тематике я не нашел. Разве только презентация Brian Goetz "Language / Library / VM co-evolution in Java SE 8" с конференции Devoxx 2011. Ну а так же исходный код будущей Java SE 8 ;) Так что пока ждемс ...

Комментариев нет:

Отправить комментарий