По результатам выступлений на конференции
"Сиклум 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 ;)
Так что пока ждемс ...
Комментариев нет:
Отправить комментарий