SlideShare ist ein Scribd-Unternehmen logo
1 von 80
Downloaden Sie, um offline zu lesen
Лямбды
в Java 8
—
Чашников Николай
1
8 2
Что?
Зачем?
Как?
Когда?
Где?
3
Что?
Runnable r =
() -> System.out.println("Hello");
4
Нет функциональных типов
package java.util.function;
public interface Function<T, R> {
R apply(T t);
}
5
Специализации для примитивов
43 интерфейса в java.util.function: *Supplier, *Consumer, *Predicate, *Operator,
*Function.
Есть все варианты
(void|int|long|double|T) -> (void|boolean|int|long|double|T)
И ещё несколько для функций с двумя параметрами.
6
Приходится везде указывать вариантность
7
Ковариантная позиция
void foo(Supplier<PsiElement> factory) {
PsiElement element = factory.get();
...
}
void bar() {
Supplier<PsiClass> classes = ...
foo(classes); // <- не скомпилируется
}
8
Ковариантная позиция
void foo(Supplier<? extends PsiElement> factory) {
PsiElement element = factory.get();
...
}
void bar() {
Supplier<PsiClass> classes = ...
foo(classes); // <- теперь работает
}
9
Контравариантная позиция
void foo(Consumer<PsiClass> consumer) {
PsiClass e = ...;
consumer.accept(e);
}
void bar() {
Consumer<PsiElement> consumer = ...
foo(consumer); // <- не скомпилируется
}
10
Контравариантная позиция
void foo(Consumer<? super PsiClass> consumer) {
PsiClass e = ...;
consumer.accept(e);
}
void bar() {
Consumer<PsiElement> consumer = ...
foo(consumer); // <- теперь работает
}
11
Бывает сразу и то, и то
default V computeIfAbsent(K key,
Function<? super K, ? extends V> mappingFunction){
}
12
Правило PECS
Producer extends, consumer super.
13
Обычно легко исправить
Добавление ? extends и ? super в параметры вызываемых методов не
ломает ни binary-compatibility, ни source-compatibility. (Если у них нет
наследников.)
14
Зачем
Чтобы писать более простой для понимания код, скрывая низкоуровневые
детали.
15
Когда-то мы писали так
void print(List<String> strings) {
for (int i = 0; i < strings.size(); i++) {
String s = strings.get(i);
System.out.println(s);
}
}
16
Теперь пишем так
void print(List<String> strings) {
for (String s : strings) {
System.out.println(s);
}
}
17
А если что-то более сложное?
void move(List<PsiClass> classes) {
boolean containsAnonymous = false;
for (PsiClass aClass : classes) {
if (aClass instanceof PsiAnonymousClass) {
containsAnonymous = true;
break;
}
}
...
}
18
Теперь можно написать так
void move(List<PsiClass> classes) {
boolean containsAnonymous =
classes.stream().anyMatch(
c -> c instanceof PsiAnonymousClass
);
...
}
19
Код может быть и более сложным
void move(List<PsiClass> classes) {
int numberOfAnonymouses = 0;
for (PsiClass aClass : classes) {
if (aClass instanceof PsiAnonymousClass) {
numberOfAnonymouses++;
if (numberOfAnonymouses >= 2) {
break;
}
}
}
...
}
20
И его все равно можно упростить
void move(List<PsiClass> classes) {
long numberOfAnonymouses = classes.stream()
.filter(c -> c instanceof PsiAnonymousClass)
.limit(2)
.count();
...
}
21
Всегда ли Stream API упрощает?
void foo(List<PsiMethod> methods) {
List<PsiMethod> constructors =
methods.stream().filter(PsiMethod::isConstructor)
.collect(Collectors.toList());
}
void foo(List<PsiMethod> methods) {
List<PsiMethod> constructors =
ContainerUtil.filter(methods, PsiMethod::isConstructor);
}
22
Всегда ли Stream API упрощает?
void foo(List<PsiMethod> methods) {
List<PsiMethod> constructors =
methods.stream().filter(PsiMethod::isConstructor)
.collect(Collectors.toList());
}
void foo(List<PsiMethod> methods) {
List<PsiMethod> constructors =
ContainerUtil.filter(methods, PsiMethod::isConstructor);
}
23
Что делает этот метод?
Object bar(PsiElement element) {
return
Optional.ofNullable(element.getContainingFile())
.map(PsiFile::getVirtualFile)
.map(f -> ModuleUtilCore.findModuleForFile(f,
element.getProject()))
.orElse(null);
}
24
Это может быть не сразу очевидно
Module findModule(PsiElement element) {
return
Optional.ofNullable(element.getContainingFile())
.map(PsiFile::getVirtualFile)
.map(f -> ModuleUtilCore.findModuleForFile(f,
element.getProject()))
.orElse(null);
}
25
Тот же результат
Module findModule(PsiElement element) {
PsiFile file = element.getContainingFile();
if (file == null) return null;
VirtualFile virtualFile = file.getVirtualFile();
if (virtualFile == null) return null;
return ModuleUtilCore.findModuleForFile(virtualFile,
element.getProject());
}
26
Другой пример
return Optional
.ofNullable(PyUtil.as(element,
PySubscriptionExpression.class))
.map(PySubscriptionExpression::getOperand)
.map(PyExpression::getText)
.filter(text -> text.equals(keywordContainerName))
.isPresent();
27
Сокращается ещё больше
return element instanceof PySubscriptionExpression
&& keywordContainerName.equals(
((PySubscriptionExpression)element).getOperand().getText());
28
Замены Optional
Вместо Optional можно использовать @Nullable типы, особенно в сигнатурах
методов.
29
Часто есть несколько вариантов
void foo(List<VirtualFile> files) {
...
files.stream().map(VirtualFile::getFileType)
.anyMatch(StdFileTypes.XML::equals)
files.stream().map(VirtualFile::getFileType)
.anyMatch(Predicate.isEqual(StdFileTypes.XML));
files.stream().anyMatch(file ->
file.getFileType().equals(StdFileTypes.XML));
...
}
30
Легко получить запутанный код
Arrays.stream(PhpLibraryRoot. EP_NAME.getExtensions()).
map(PhpLibraryRoot::getProvider).
filter(PhpLibraryRootProvider::isRuntime).
map(provider -> provider.getLibraryRoot(getProject())).
filter(Optional::isPresent).
map(Optional::get).
map(VirtualFile::getChildren).
map(Arrays:: asList).
flatMap(Collection::stream).
filter(VirtualFile::isDirectory).
filter(module -> ! ".idea".equals(module.getName())).
...
31
IDEA 2017.1 поможет его упростить
32
В результате получим
Arrays.stream(PhpLibraryRoot. EP_NAME.getExtensions())
.map(PhpLibraryRoot::getProvider)
.filter(PhpLibraryRootProvider::isRuntime)
.map(provider -> provider.getLibraryRoot(getProject()))
.filter(Objects:: nonNull)
.flatMap(root -> Arrays. stream(root.getChildren()))
.filter(file -> file.isDirectory() && ! ".idea".equals(file.getName()))
...
33
Где
Где ещё могут быть полезны лямбды?
34
Простая параметризация значением
TreeNode buildTree(PsiClass aClass) {
...
for (PsiMethod method : aClass.getMethods()) {
root.add(createMethodNode(method));
}
...
}
35
Убрать статические методы
TreeNode buildTree(PsiClass aClass,
boolean withStatic) {
...
for (PsiMethod method : aClass.getMethods()) {
if (withStatic || !method.getModifierList()
.hasModifierProperty(PsiModifier.STATIC)) {
root.add(createMethodNode(method));
}
}
...
}
36
Следующая ступень обобщения
abstract class TreeBuilder {
protected abstract boolean acceptMethod(PsiMethod method);
TreeNode buildTree(PsiClass aClass) {
...
for (PsiMethod method : aClass.getMethods()) {
if (acceptMethod(method)) {
root.add(createMethodNode(method));
}
}
...
}
}
37
Переусложнено и выглядит громоздко
new TreeBuilder() {
@Override
protected boolean acceptMethod(PsiMethod method) {
return !method.getModifierList()
.hasModifierProperty(PsiModifier.STATIC);
}
}.buildTree(aClass);
38
Параметризация поведением
39
Параметризация поведением
TreeNode buildTree(PsiClass aClass,
Predicate<PsiMethod> methodFilter) {
...
for (PsiMethod method : aClass.getMethods()) {
if (methodFilter.test(method)) {
root.add(createMethodNode(method));
}
}
...
}
40
Использовать тоже легко
buildTree(aClass,
method -> !method.getModifierList()
.hasModifierProperty(PsiModifier.STATIC));
41
Таких случаев много
public abstract class NotNullLazyValue<T> {
private T myValue;
@NotNull
protected abstract T compute();
...
}
42
Теперь можно создать без наследования
public abstract class NotNullLazyValue<T> {
...
@NotNull
public static <T> NotNullLazyValue<T>
createValue(@NotNull NotNullFactory<T> value) {
...
};
}
43
Есть более сложные случаи
Класс FileReferenceSet, больше 100 наследников, половина из них –
анонимные классы, 23 метода, которые переопределяются в наследниках.
44
Выглядят вот так
return new FileReferenceSet(...) {
protected boolean isSoft() { return soft; }
public boolean isAbsolutePathReference() { return true; }
public boolean couldBeConvertedTo(boolean relative) {...}
public boolean absoluteUrlNeedsStartSlash() {
String s = getPathString();
return s != null && !s.isEmpty() && s.charAt(0) == '/';
}
public Collection<PsiFileSystemItem>
computeDefaultContexts() {...}
}.getAllReferences();
45
Как
Как лямбды устроены внутри?
46
Есть ли здесь утечка памяти?
public class Foo {
void foo() {
Runnable r = new Runnable() {
public void run() {
System.out.println("Bye!");
}
};
Runtime.getRuntime()
.addShutdownHook(new Thread(r));
}
...
}
47
Да, есть ссылка на внешний объект
class Foo$1 implements Runnable {
final Foo this$0;
public Foo$1(Foo foo) {
this$0 = foo;
}
...
}
48
Экземпляр создаётся каждый раз
ContainerUtil.filter(methods,
new Condition<PsiMethod>() {
public boolean value(PsiMethod psiMethod) {
return psiMethod.isConstructor();
}
}
);
49
Во что компилировать лябмды?
public class Simple {
public static void main(String[] args) {
Runnable r =
() -> System.out.println("Hello");
new Thread(r).start();
}
}
50
Самая простая реализация – анонимус
public class Simple {
public static void main(String[] args) {
Runnable r = new Runnable() {
public void run() {
System.out.println("Hello");
}
}
new Thread(r).start();
}
}
51
У такой реализации есть недостатки
● лишний class-файл на диске
● каждый раз создаётся новый экземпляр
● Class не будет собран в мусор, пока достижим его ClassLoader
52
Анонимны ли анонимусы?
Class.forName("Simple$1",
true, Simple.class.getClassLoader())
53
Нужен ли вообще класс для лямбды?
Да, ведь можно вызвать r.getClass().
r.getClass().getName()вернёт что-то вроде
"Simple$$Lambda$1/1149319664"
Unsafe.getUnsafe().defineAnonymousClass(...)
54
Классы лямбд – действительно анонимны
boolean isLambda(Runnable r) {
try {
Class.forName(r.getClass().getName(), false,
r.getClass().getClassLoader());
return false;
}
catch (ClassNotFoundException e) {
return true;
}
}
55
Кто будет генерировать код, создающий лямбду?
Записывать этот код в каждый класс неэффективно. Требуется общий метод
в стандартной библиотеке, который будет создавать все классы лямбд.
Назовём его LambdaMetafactory.metafactory.
Но в него надо как-то передавать информацию про то, какой метод какого
интерфейса должна реализовывать лямбда, и код её тела.
56
Для начала сделаем desugaring
public class Simple {
public static void main(String[] args) {
Runnable r = Simple::lambda$main$0;
new Thread(r).start();
}
private static void lambda$main$0() {
System.out.println("Hello");
}
}
57
MethodHandle
public abstract class MethodHandle {
@PolymorphicSignature
public final native Object invoke(Object... args)
throws Throwable;
public MethodType type() { ... }
...
}
public class MethodType {
public static MethodType methodType(Class<?> rtype) {...}
public static MethodType methodType(Class<?> rtype,
Class<?> ptype0) {...}
...
}
58
Отличия от java.lang.reflect.Method
● проверка доступа в момент создания, а не вызова
● работают не только с методами, но и с полями, конструкторами, …
● могут делать преобразования параметров и возвращаемого значения
● работают с примитивными типами напрямую, без заворачивания в
объекты
● не создаётся массив для передачи аргументов
59
Реализация лямбды, попытка 1
public class LambdaMetafactory {
public static Object metafactory0(
String samMethodName,
Class<?> samType,
MethodType samMethodType,
MethodHandle implMethod) {
...
Class cls = Unsafe.getUnsafe()
.defineAnonymousClass(...);
return cls.newInstance();
}
} 60
Реализация лямбды, попытка 1
public class Simple {
public static void main(String[] args) throws Throwable {
MethodHandle implMethod =
MethodHandles.lookup().findStatic(Simple.class,
"lambda$main$0", MethodType.methodType(void.class));
MethodType samMethodType = MethodType.methodType(void.class);
Runnable r = (Runnable) LambdaMetafactory.metafactory0(
"run", Runnable.class, samMethodType, implMethod);
new Thread(r).start();
}
private static void lambda$main$0() {
System.out.println("Hello");
}
}
61
Попытка 2: кэшируем лямбду
public class Simple {
private static Object[] lambdas = new Object[1];
public static void main(String[] args) throws Throwable {
if (lambdas[0] == null) {
...
lambdas[0] = LambdaMetafactory.metafactory0(
"run", Runnable.class, samMethodType, implMethod);
}
Runnable r = (Runnable) lambdas[0];
new Thread(r).start();
}
...
}
62
CallSite
public abstract class CallSite {
public abstract MethodHandle getTarget();
...
}
public class ConstantCallSite extends CallSite {
public ConstantCallSite(MethodHandle target) {...}
}
63
Попытка 3: invokeDynamic
public class Simple {
private static CallSite[] callSites = new CallSite[1];
public static void main(String[] args) throws Throwable {
if (callSites[0] == null) {
MethodHandle implMethod = MethodHandles. lookup().findStatic(
Simple. class, "lambda$main$0", MethodType.methodType(void.class));
MethodType samMethodType = MethodType. methodType(void.class);
callSites[0] = LambdaMetafactory. metafactory(
MethodHandles. lookup(),
"run", MethodType.methodType(Runnable.class),
samMethodType, implMethod, samMethodType);
}
Runnable r = (Runnable) callSites[0].getTarget().invoke();
...
}
...
64
Попытка 3: invokeDynamic
public class Simple {
private static CallSite[] callSites = new CallSite[1];
public static void main(String[] args) throws Throwable {
if (callSites[0] == null) {
MethodHandle implMethod = MethodHandles. lookup().findStatic(
Simple.class, "lambda$main$0", MethodType.methodType(void.class));
MethodType samMethodType = MethodType.methodType(void.class);
callSites[0] = LambdaMetafactory. metafactory(
MethodHandles. lookup(),
"run", MethodType.methodType(Runnable.class),
samMethodType, implMethod, samMethodType);
}
Runnable r = (Runnable) callSites[0].getTarget().invoke() ;
...
}
...
65
Попытка 3: реальная metafactory
static CallSite metafactory(MethodHandles.Lookup caller,
String invokedName, MethodType invokedType,
MethodType samMethodType, MethodHandle implMethod,
MethodType instantiatedMethodType) {
Class cls = Unsafe.getUnsafe()
.defineAnonymousClass(...);
if (invokedType.parameterCount() == 0) {
Object instance = cls.newInstance();
return new ConstantCallSite(
MethodHandles.constant(samType, instance));
}
...
}
66
Замыкания (capturing lambdas)
public class CapturingLambda {
public void foo(String name) {
Runnable r = () ->
System.out.println("Hello, " + name);
new Thread(r).start();
}
}
67
Замыкания: desugaring
public class CapturingLambda {
public void foo(String name) {
Runnable r = () -> lambda$foo$0(name);
new Thread(r).start();
}
private static void lambda$foo$0(String name) {
System.out.println("Hello, " + name);
}
}
68
Замыкания: класс лямбды
final class CapturingLambda$$Lambda$1 implements Runnable {
private final String arg$1;
private CapturingLambda$$Lambda$1(String s) {
arg$1 = s;
}
private static Runnable get$Lambda(String s) {
return new CapturingLambda$$Lambda$1(s);
}
public void run() {
System.out.println("Hello, " + arg$1);
}
}
69
Замыкания: кэширование в metafactory
static CallSite metafactory(MethodHandles.Lookup caller,
String invokedName, MethodType invokedType,
MethodType samMethodType, MethodHandle implMethod,
MethodType instantiatedMethodType) {
Class cls = Unsafe.getUnsafe()
.defineAnonymousClass(...);
if (invokedType.parameterCount() == 0) {...}
else {
return new ConstantCallSite(
MethodHandles.Lookup.IMPL_LOOKUP.findStatic(cls,"get$Lambda",
invokedType));
}
}
70
Замыкания: invokeDynamic
public class CapturingLambda {
private static CallSite[] callSites = new CallSite[1];
public void foo(String name) throws Throwable {
if (callSites[0] == null) {
MethodHandle implMethod =
MethodHandles. lookup().findStatic(CapturingLambda. class,
"lambda$foo$0", MethodType.methodType(void.class, String.class));
MethodType samMethodType = MethodType. methodType(void.class);
callSites[0] = LambdaMetafactory. metafactory(MethodHandles. lookup(),
"run", MethodType.methodType(Runnable.class, String.class),
samMethodType, implMethod, samMethodType);
}
Runnable r = (Runnable) callSites[0].getTarget().invoke(name);
...
71
Ответы
72
Утечки памяти нет
public class Foo {
void foo() {
Runnable r =() -> System.out.println("Bye!");
Runtime.getRuntime()
.addShutdownHook(new Thread(r));
}
...
}
73
Экземпляр для non-capturing переиспользуется
ContainerUtil.filter(methods,
method -> method.isConstructor();
);
ContainerUtil.filter(methods,PsiMethod::isConstructor)
74
Это может приводить к проблемам
public interface Disposable {
void dispose();
}
public class Disposer {
public static void register(Disposable parent,
Disposable child) {
...
map.put(parent, child);
}
}
75
Если лямбды сравниваются как объекты
private final Disposable parent = new Disposable() {
public void dispose() { }
};
void foo() {
Disposer.register(parent, ...);
}
private final Disposable parent = () -> { };
void foo() {
Disposer.register(parent, ...);
}
76
Лямбды работают достаточно эффективно
И создание, и вызовы хорошо инлайнятся.
77
Ссылки
Refactoring to Functional Style with Java 8 by Venkat Subramaniam
Stream API, часть 1, Сергей Куксенко
Stream API: Tagir Valeev
Translation of Lambda Expressions by Brian Goetz
Глубокое погружение в invokedynamic, Владимир Иванов
78
Итоги
● используйте лямбды и стримы, чтобы писать более высокоуровневый и
понятный код
● но не увлекайтесь, функциональный стиль – не самоцель, а лишь
средство
● изучайте, как это устроено; особенно если натыкаетесь на непонятное
поведение
79
Ещё вопросы?
80

Weitere ähnliche Inhalte

Was ist angesagt?

Характерные черты функциональных языков программирования
Характерные черты функциональных языков программированияХарактерные черты функциональных языков программирования
Характерные черты функциональных языков программированияAlex.Kolonitsky
 
Java осень 2014 занятие 3
Java осень 2014 занятие 3Java осень 2014 занятие 3
Java осень 2014 занятие 3Technopark
 
Лекция 12. Быстрее, Python, ещё быстрее.
Лекция 12. Быстрее, Python, ещё быстрее.Лекция 12. Быстрее, Python, ещё быстрее.
Лекция 12. Быстрее, Python, ещё быстрее.Roman Brovko
 
Лекция 9. Модули, пакеты и система импорта.
Лекция 9. Модули, пакеты и система импорта.Лекция 9. Модули, пакеты и система импорта.
Лекция 9. Модули, пакеты и система импорта.Roman Brovko
 
Объектно-ориентированное программирование. Лекция 5 и 6
Объектно-ориентированное программирование. Лекция 5 и 6Объектно-ориентированное программирование. Лекция 5 и 6
Объектно-ориентированное программирование. Лекция 5 и 6Dima Dzuba
 
Discovering Lambdas in Java 8
Discovering Lambdas in Java 8Discovering Lambdas in Java 8
Discovering Lambdas in Java 8Stfalcon Meetups
 
07 - Java. Элементы функционального программирования в Java
07 - Java. Элементы функционального программирования в Java07 - Java. Элементы функционального программирования в Java
07 - Java. Элементы функционального программирования в JavaRoman Brovko
 
основы Java переменные, циклы
основы Java   переменные, циклыосновы Java   переменные, циклы
основы Java переменные, циклыSergey Nemchinsky
 

Was ist angesagt? (9)

Характерные черты функциональных языков программирования
Характерные черты функциональных языков программированияХарактерные черты функциональных языков программирования
Характерные черты функциональных языков программирования
 
Java осень 2014 занятие 3
Java осень 2014 занятие 3Java осень 2014 занятие 3
Java осень 2014 занятие 3
 
Лекция 12. Быстрее, Python, ещё быстрее.
Лекция 12. Быстрее, Python, ещё быстрее.Лекция 12. Быстрее, Python, ещё быстрее.
Лекция 12. Быстрее, Python, ещё быстрее.
 
Лекция 9. Модули, пакеты и система импорта.
Лекция 9. Модули, пакеты и система импорта.Лекция 9. Модули, пакеты и система импорта.
Лекция 9. Модули, пакеты и система импорта.
 
Объектно-ориентированное программирование. Лекция 5 и 6
Объектно-ориентированное программирование. Лекция 5 и 6Объектно-ориентированное программирование. Лекция 5 и 6
Объектно-ориентированное программирование. Лекция 5 и 6
 
Discovering Lambdas in Java 8
Discovering Lambdas in Java 8Discovering Lambdas in Java 8
Discovering Lambdas in Java 8
 
Discovering Lambdas (Speech)
Discovering Lambdas (Speech)Discovering Lambdas (Speech)
Discovering Lambdas (Speech)
 
07 - Java. Элементы функционального программирования в Java
07 - Java. Элементы функционального программирования в Java07 - Java. Элементы функционального программирования в Java
07 - Java. Элементы функционального программирования в Java
 
основы Java переменные, циклы
основы Java   переменные, циклыосновы Java   переменные, циклы
основы Java переменные, циклы
 

Andere mochten auch

Plugin development for intelli j platform
Plugin development for intelli j platformPlugin development for intelli j platform
Plugin development for intelli j platformchashnikov
 
API design in java project
API design in java projectAPI design in java project
API design in java projectchashnikov
 
Effective coding in IntelliJ IDEA
Effective coding in IntelliJ IDEAEffective coding in IntelliJ IDEA
Effective coding in IntelliJ IDEAchashnikov
 
Java compilers and IDEs
Java compilers and IDEsJava compilers and IDEs
Java compilers and IDEschashnikov
 
Statis code analysis
Statis code analysisStatis code analysis
Statis code analysischashnikov
 
2015 Upload Campaigns Calendar - SlideShare
2015 Upload Campaigns Calendar - SlideShare2015 Upload Campaigns Calendar - SlideShare
2015 Upload Campaigns Calendar - SlideShareSlideShare
 
What to Upload to SlideShare
What to Upload to SlideShareWhat to Upload to SlideShare
What to Upload to SlideShareSlideShare
 
How to Make Awesome SlideShares: Tips & Tricks
How to Make Awesome SlideShares: Tips & TricksHow to Make Awesome SlideShares: Tips & Tricks
How to Make Awesome SlideShares: Tips & TricksSlideShare
 
Getting Started With SlideShare
Getting Started With SlideShareGetting Started With SlideShare
Getting Started With SlideShareSlideShare
 

Andere mochten auch (9)

Plugin development for intelli j platform
Plugin development for intelli j platformPlugin development for intelli j platform
Plugin development for intelli j platform
 
API design in java project
API design in java projectAPI design in java project
API design in java project
 
Effective coding in IntelliJ IDEA
Effective coding in IntelliJ IDEAEffective coding in IntelliJ IDEA
Effective coding in IntelliJ IDEA
 
Java compilers and IDEs
Java compilers and IDEsJava compilers and IDEs
Java compilers and IDEs
 
Statis code analysis
Statis code analysisStatis code analysis
Statis code analysis
 
2015 Upload Campaigns Calendar - SlideShare
2015 Upload Campaigns Calendar - SlideShare2015 Upload Campaigns Calendar - SlideShare
2015 Upload Campaigns Calendar - SlideShare
 
What to Upload to SlideShare
What to Upload to SlideShareWhat to Upload to SlideShare
What to Upload to SlideShare
 
How to Make Awesome SlideShares: Tips & Tricks
How to Make Awesome SlideShares: Tips & TricksHow to Make Awesome SlideShares: Tips & Tricks
How to Make Awesome SlideShares: Tips & Tricks
 
Getting Started With SlideShare
Getting Started With SlideShareGetting Started With SlideShare
Getting Started With SlideShare
 

Ähnlich wie Lambdas in java 8

Статический и динамический полиморфизм в C++, Дмитрий Леванов
Статический и динамический полиморфизм в C++, Дмитрий ЛевановСтатический и динамический полиморфизм в C++, Дмитрий Леванов
Статический и динамический полиморфизм в C++, Дмитрий ЛевановYandex
 
паттерны программирования
паттерны программированияпаттерны программирования
паттерны программированияguestfc8ae0
 
C++ STL & Qt. Занятие 02.
C++ STL & Qt. Занятие 02.C++ STL & Qt. Занятие 02.
C++ STL & Qt. Занятие 02.Igor Shkulipa
 
C++ STL & Qt. Занятие 01.
C++ STL & Qt. Занятие 01.C++ STL & Qt. Занятие 01.
C++ STL & Qt. Занятие 01.Igor Shkulipa
 
Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014
Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014
Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014Python Meetup
 
Что нам стоит DAL построить? Акуляков Артём D2D Just.NET
Что нам стоит DAL построить? Акуляков Артём D2D Just.NETЧто нам стоит DAL построить? Акуляков Артём D2D Just.NET
Что нам стоит DAL построить? Акуляков Артём D2D Just.NETDev2Dev
 
Лекция 8. Итераторы, генераторы и модуль itertools.
 Лекция 8. Итераторы, генераторы и модуль itertools. Лекция 8. Итераторы, генераторы и модуль itertools.
Лекция 8. Итераторы, генераторы и модуль itertools.Roman Brovko
 
разработка серверов и серверных приложений лекция №3
разработка серверов и серверных приложений лекция №3разработка серверов и серверных приложений лекция №3
разработка серверов и серверных приложений лекция №3etyumentcev
 
разработка серверов и серверных приложений лекция №3
разработка серверов и серверных приложений лекция №3разработка серверов и серверных приложений лекция №3
разработка серверов и серверных приложений лекция №3Eugeniy Tyumentcev
 
JPoint 2016 - Etudes of DIY Java profiler
JPoint 2016 - Etudes of DIY Java profilerJPoint 2016 - Etudes of DIY Java profiler
JPoint 2016 - Etudes of DIY Java profilerAnton Arhipov
 
Rust: абстракции и безопасность, совершенно бесплатно
Rust: абстракции и безопасность, совершенно бесплатноRust: абстракции и безопасность, совершенно бесплатно
Rust: абстракции и безопасность, совершенно бесплатноOpen-IT
 
Статический и динамический полиморфизм в C++, Дмитрий Леванов
Статический и динамический полиморфизм в C++, Дмитрий ЛевановСтатический и динамический полиморфизм в C++, Дмитрий Леванов
Статический и динамический полиморфизм в C++, Дмитрий ЛевановYandex
 
Java осень 2014 занятие 5
Java осень 2014 занятие 5Java осень 2014 занятие 5
Java осень 2014 занятие 5Technopark
 
C# Desktop. Занятие 06.
C# Desktop. Занятие 06.C# Desktop. Занятие 06.
C# Desktop. Занятие 06.Igor Shkulipa
 
Java осень 2013 лекция 2
Java осень 2013 лекция 2Java осень 2013 лекция 2
Java осень 2013 лекция 2Technopark
 
Очень вкусный фрукт Guava
Очень вкусный фрукт GuavaОчень вкусный фрукт Guava
Очень вкусный фрукт GuavaEgor Chernyshev
 
[JAM 1.1] Clean Code (Paul Malikov)
[JAM 1.1] Clean Code (Paul Malikov)[JAM 1.1] Clean Code (Paul Malikov)
[JAM 1.1] Clean Code (Paul Malikov)Evgeny Kaziak
 
CPU Performance in Java.
CPU Performance in Java.CPU Performance in Java.
CPU Performance in Java.Dzmitry Hil
 

Ähnlich wie Lambdas in java 8 (20)

Статический и динамический полиморфизм в C++, Дмитрий Леванов
Статический и динамический полиморфизм в C++, Дмитрий ЛевановСтатический и динамический полиморфизм в C++, Дмитрий Леванов
Статический и динамический полиморфизм в C++, Дмитрий Леванов
 
паттерны программирования
паттерны программированияпаттерны программирования
паттерны программирования
 
C++ STL & Qt. Занятие 02.
C++ STL & Qt. Занятие 02.C++ STL & Qt. Занятие 02.
C++ STL & Qt. Занятие 02.
 
Scala on android
Scala on androidScala on android
Scala on android
 
C++ STL & Qt. Занятие 01.
C++ STL & Qt. Занятие 01.C++ STL & Qt. Занятие 01.
C++ STL & Qt. Занятие 01.
 
Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014
Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014
Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014
 
Что нам стоит DAL построить? Акуляков Артём D2D Just.NET
Что нам стоит DAL построить? Акуляков Артём D2D Just.NETЧто нам стоит DAL построить? Акуляков Артём D2D Just.NET
Что нам стоит DAL построить? Акуляков Артём D2D Just.NET
 
Лекция 8. Итераторы, генераторы и модуль itertools.
 Лекция 8. Итераторы, генераторы и модуль itertools. Лекция 8. Итераторы, генераторы и модуль itertools.
Лекция 8. Итераторы, генераторы и модуль itertools.
 
разработка серверов и серверных приложений лекция №3
разработка серверов и серверных приложений лекция №3разработка серверов и серверных приложений лекция №3
разработка серверов и серверных приложений лекция №3
 
разработка серверов и серверных приложений лекция №3
разработка серверов и серверных приложений лекция №3разработка серверов и серверных приложений лекция №3
разработка серверов и серверных приложений лекция №3
 
JPoint 2016 - Etudes of DIY Java profiler
JPoint 2016 - Etudes of DIY Java profilerJPoint 2016 - Etudes of DIY Java profiler
JPoint 2016 - Etudes of DIY Java profiler
 
Rust: абстракции и безопасность, совершенно бесплатно
Rust: абстракции и безопасность, совершенно бесплатноRust: абстракции и безопасность, совершенно бесплатно
Rust: абстракции и безопасность, совершенно бесплатно
 
Статический и динамический полиморфизм в C++, Дмитрий Леванов
Статический и динамический полиморфизм в C++, Дмитрий ЛевановСтатический и динамический полиморфизм в C++, Дмитрий Леванов
Статический и динамический полиморфизм в C++, Дмитрий Леванов
 
Java осень 2014 занятие 5
Java осень 2014 занятие 5Java осень 2014 занятие 5
Java осень 2014 занятие 5
 
C# Desktop. Занятие 06.
C# Desktop. Занятие 06.C# Desktop. Занятие 06.
C# Desktop. Занятие 06.
 
Java осень 2013 лекция 2
Java осень 2013 лекция 2Java осень 2013 лекция 2
Java осень 2013 лекция 2
 
Bytecode
BytecodeBytecode
Bytecode
 
Очень вкусный фрукт Guava
Очень вкусный фрукт GuavaОчень вкусный фрукт Guava
Очень вкусный фрукт Guava
 
[JAM 1.1] Clean Code (Paul Malikov)
[JAM 1.1] Clean Code (Paul Malikov)[JAM 1.1] Clean Code (Paul Malikov)
[JAM 1.1] Clean Code (Paul Malikov)
 
CPU Performance in Java.
CPU Performance in Java.CPU Performance in Java.
CPU Performance in Java.
 

Lambdas in java 8

  • 2. 8 2
  • 4. Что? Runnable r = () -> System.out.println("Hello"); 4
  • 5. Нет функциональных типов package java.util.function; public interface Function<T, R> { R apply(T t); } 5
  • 6. Специализации для примитивов 43 интерфейса в java.util.function: *Supplier, *Consumer, *Predicate, *Operator, *Function. Есть все варианты (void|int|long|double|T) -> (void|boolean|int|long|double|T) И ещё несколько для функций с двумя параметрами. 6
  • 8. Ковариантная позиция void foo(Supplier<PsiElement> factory) { PsiElement element = factory.get(); ... } void bar() { Supplier<PsiClass> classes = ... foo(classes); // <- не скомпилируется } 8
  • 9. Ковариантная позиция void foo(Supplier<? extends PsiElement> factory) { PsiElement element = factory.get(); ... } void bar() { Supplier<PsiClass> classes = ... foo(classes); // <- теперь работает } 9
  • 10. Контравариантная позиция void foo(Consumer<PsiClass> consumer) { PsiClass e = ...; consumer.accept(e); } void bar() { Consumer<PsiElement> consumer = ... foo(consumer); // <- не скомпилируется } 10
  • 11. Контравариантная позиция void foo(Consumer<? super PsiClass> consumer) { PsiClass e = ...; consumer.accept(e); } void bar() { Consumer<PsiElement> consumer = ... foo(consumer); // <- теперь работает } 11
  • 12. Бывает сразу и то, и то default V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction){ } 12
  • 14. Обычно легко исправить Добавление ? extends и ? super в параметры вызываемых методов не ломает ни binary-compatibility, ни source-compatibility. (Если у них нет наследников.) 14
  • 15. Зачем Чтобы писать более простой для понимания код, скрывая низкоуровневые детали. 15
  • 16. Когда-то мы писали так void print(List<String> strings) { for (int i = 0; i < strings.size(); i++) { String s = strings.get(i); System.out.println(s); } } 16
  • 17. Теперь пишем так void print(List<String> strings) { for (String s : strings) { System.out.println(s); } } 17
  • 18. А если что-то более сложное? void move(List<PsiClass> classes) { boolean containsAnonymous = false; for (PsiClass aClass : classes) { if (aClass instanceof PsiAnonymousClass) { containsAnonymous = true; break; } } ... } 18
  • 19. Теперь можно написать так void move(List<PsiClass> classes) { boolean containsAnonymous = classes.stream().anyMatch( c -> c instanceof PsiAnonymousClass ); ... } 19
  • 20. Код может быть и более сложным void move(List<PsiClass> classes) { int numberOfAnonymouses = 0; for (PsiClass aClass : classes) { if (aClass instanceof PsiAnonymousClass) { numberOfAnonymouses++; if (numberOfAnonymouses >= 2) { break; } } } ... } 20
  • 21. И его все равно можно упростить void move(List<PsiClass> classes) { long numberOfAnonymouses = classes.stream() .filter(c -> c instanceof PsiAnonymousClass) .limit(2) .count(); ... } 21
  • 22. Всегда ли Stream API упрощает? void foo(List<PsiMethod> methods) { List<PsiMethod> constructors = methods.stream().filter(PsiMethod::isConstructor) .collect(Collectors.toList()); } void foo(List<PsiMethod> methods) { List<PsiMethod> constructors = ContainerUtil.filter(methods, PsiMethod::isConstructor); } 22
  • 23. Всегда ли Stream API упрощает? void foo(List<PsiMethod> methods) { List<PsiMethod> constructors = methods.stream().filter(PsiMethod::isConstructor) .collect(Collectors.toList()); } void foo(List<PsiMethod> methods) { List<PsiMethod> constructors = ContainerUtil.filter(methods, PsiMethod::isConstructor); } 23
  • 24. Что делает этот метод? Object bar(PsiElement element) { return Optional.ofNullable(element.getContainingFile()) .map(PsiFile::getVirtualFile) .map(f -> ModuleUtilCore.findModuleForFile(f, element.getProject())) .orElse(null); } 24
  • 25. Это может быть не сразу очевидно Module findModule(PsiElement element) { return Optional.ofNullable(element.getContainingFile()) .map(PsiFile::getVirtualFile) .map(f -> ModuleUtilCore.findModuleForFile(f, element.getProject())) .orElse(null); } 25
  • 26. Тот же результат Module findModule(PsiElement element) { PsiFile file = element.getContainingFile(); if (file == null) return null; VirtualFile virtualFile = file.getVirtualFile(); if (virtualFile == null) return null; return ModuleUtilCore.findModuleForFile(virtualFile, element.getProject()); } 26
  • 28. Сокращается ещё больше return element instanceof PySubscriptionExpression && keywordContainerName.equals( ((PySubscriptionExpression)element).getOperand().getText()); 28
  • 29. Замены Optional Вместо Optional можно использовать @Nullable типы, особенно в сигнатурах методов. 29
  • 30. Часто есть несколько вариантов void foo(List<VirtualFile> files) { ... files.stream().map(VirtualFile::getFileType) .anyMatch(StdFileTypes.XML::equals) files.stream().map(VirtualFile::getFileType) .anyMatch(Predicate.isEqual(StdFileTypes.XML)); files.stream().anyMatch(file -> file.getFileType().equals(StdFileTypes.XML)); ... } 30
  • 31. Легко получить запутанный код Arrays.stream(PhpLibraryRoot. EP_NAME.getExtensions()). map(PhpLibraryRoot::getProvider). filter(PhpLibraryRootProvider::isRuntime). map(provider -> provider.getLibraryRoot(getProject())). filter(Optional::isPresent). map(Optional::get). map(VirtualFile::getChildren). map(Arrays:: asList). flatMap(Collection::stream). filter(VirtualFile::isDirectory). filter(module -> ! ".idea".equals(module.getName())). ... 31
  • 32. IDEA 2017.1 поможет его упростить 32
  • 33. В результате получим Arrays.stream(PhpLibraryRoot. EP_NAME.getExtensions()) .map(PhpLibraryRoot::getProvider) .filter(PhpLibraryRootProvider::isRuntime) .map(provider -> provider.getLibraryRoot(getProject())) .filter(Objects:: nonNull) .flatMap(root -> Arrays. stream(root.getChildren())) .filter(file -> file.isDirectory() && ! ".idea".equals(file.getName())) ... 33
  • 34. Где Где ещё могут быть полезны лямбды? 34
  • 35. Простая параметризация значением TreeNode buildTree(PsiClass aClass) { ... for (PsiMethod method : aClass.getMethods()) { root.add(createMethodNode(method)); } ... } 35
  • 36. Убрать статические методы TreeNode buildTree(PsiClass aClass, boolean withStatic) { ... for (PsiMethod method : aClass.getMethods()) { if (withStatic || !method.getModifierList() .hasModifierProperty(PsiModifier.STATIC)) { root.add(createMethodNode(method)); } } ... } 36
  • 37. Следующая ступень обобщения abstract class TreeBuilder { protected abstract boolean acceptMethod(PsiMethod method); TreeNode buildTree(PsiClass aClass) { ... for (PsiMethod method : aClass.getMethods()) { if (acceptMethod(method)) { root.add(createMethodNode(method)); } } ... } } 37
  • 38. Переусложнено и выглядит громоздко new TreeBuilder() { @Override protected boolean acceptMethod(PsiMethod method) { return !method.getModifierList() .hasModifierProperty(PsiModifier.STATIC); } }.buildTree(aClass); 38
  • 40. Параметризация поведением TreeNode buildTree(PsiClass aClass, Predicate<PsiMethod> methodFilter) { ... for (PsiMethod method : aClass.getMethods()) { if (methodFilter.test(method)) { root.add(createMethodNode(method)); } } ... } 40
  • 41. Использовать тоже легко buildTree(aClass, method -> !method.getModifierList() .hasModifierProperty(PsiModifier.STATIC)); 41
  • 42. Таких случаев много public abstract class NotNullLazyValue<T> { private T myValue; @NotNull protected abstract T compute(); ... } 42
  • 43. Теперь можно создать без наследования public abstract class NotNullLazyValue<T> { ... @NotNull public static <T> NotNullLazyValue<T> createValue(@NotNull NotNullFactory<T> value) { ... }; } 43
  • 44. Есть более сложные случаи Класс FileReferenceSet, больше 100 наследников, половина из них – анонимные классы, 23 метода, которые переопределяются в наследниках. 44
  • 45. Выглядят вот так return new FileReferenceSet(...) { protected boolean isSoft() { return soft; } public boolean isAbsolutePathReference() { return true; } public boolean couldBeConvertedTo(boolean relative) {...} public boolean absoluteUrlNeedsStartSlash() { String s = getPathString(); return s != null && !s.isEmpty() && s.charAt(0) == '/'; } public Collection<PsiFileSystemItem> computeDefaultContexts() {...} }.getAllReferences(); 45
  • 47. Есть ли здесь утечка памяти? public class Foo { void foo() { Runnable r = new Runnable() { public void run() { System.out.println("Bye!"); } }; Runtime.getRuntime() .addShutdownHook(new Thread(r)); } ... } 47
  • 48. Да, есть ссылка на внешний объект class Foo$1 implements Runnable { final Foo this$0; public Foo$1(Foo foo) { this$0 = foo; } ... } 48
  • 49. Экземпляр создаётся каждый раз ContainerUtil.filter(methods, new Condition<PsiMethod>() { public boolean value(PsiMethod psiMethod) { return psiMethod.isConstructor(); } } ); 49
  • 50. Во что компилировать лябмды? public class Simple { public static void main(String[] args) { Runnable r = () -> System.out.println("Hello"); new Thread(r).start(); } } 50
  • 51. Самая простая реализация – анонимус public class Simple { public static void main(String[] args) { Runnable r = new Runnable() { public void run() { System.out.println("Hello"); } } new Thread(r).start(); } } 51
  • 52. У такой реализации есть недостатки ● лишний class-файл на диске ● каждый раз создаётся новый экземпляр ● Class не будет собран в мусор, пока достижим его ClassLoader 52
  • 54. Нужен ли вообще класс для лямбды? Да, ведь можно вызвать r.getClass(). r.getClass().getName()вернёт что-то вроде "Simple$$Lambda$1/1149319664" Unsafe.getUnsafe().defineAnonymousClass(...) 54
  • 55. Классы лямбд – действительно анонимны boolean isLambda(Runnable r) { try { Class.forName(r.getClass().getName(), false, r.getClass().getClassLoader()); return false; } catch (ClassNotFoundException e) { return true; } } 55
  • 56. Кто будет генерировать код, создающий лямбду? Записывать этот код в каждый класс неэффективно. Требуется общий метод в стандартной библиотеке, который будет создавать все классы лямбд. Назовём его LambdaMetafactory.metafactory. Но в него надо как-то передавать информацию про то, какой метод какого интерфейса должна реализовывать лямбда, и код её тела. 56
  • 57. Для начала сделаем desugaring public class Simple { public static void main(String[] args) { Runnable r = Simple::lambda$main$0; new Thread(r).start(); } private static void lambda$main$0() { System.out.println("Hello"); } } 57
  • 58. MethodHandle public abstract class MethodHandle { @PolymorphicSignature public final native Object invoke(Object... args) throws Throwable; public MethodType type() { ... } ... } public class MethodType { public static MethodType methodType(Class<?> rtype) {...} public static MethodType methodType(Class<?> rtype, Class<?> ptype0) {...} ... } 58
  • 59. Отличия от java.lang.reflect.Method ● проверка доступа в момент создания, а не вызова ● работают не только с методами, но и с полями, конструкторами, … ● могут делать преобразования параметров и возвращаемого значения ● работают с примитивными типами напрямую, без заворачивания в объекты ● не создаётся массив для передачи аргументов 59
  • 60. Реализация лямбды, попытка 1 public class LambdaMetafactory { public static Object metafactory0( String samMethodName, Class<?> samType, MethodType samMethodType, MethodHandle implMethod) { ... Class cls = Unsafe.getUnsafe() .defineAnonymousClass(...); return cls.newInstance(); } } 60
  • 61. Реализация лямбды, попытка 1 public class Simple { public static void main(String[] args) throws Throwable { MethodHandle implMethod = MethodHandles.lookup().findStatic(Simple.class, "lambda$main$0", MethodType.methodType(void.class)); MethodType samMethodType = MethodType.methodType(void.class); Runnable r = (Runnable) LambdaMetafactory.metafactory0( "run", Runnable.class, samMethodType, implMethod); new Thread(r).start(); } private static void lambda$main$0() { System.out.println("Hello"); } } 61
  • 62. Попытка 2: кэшируем лямбду public class Simple { private static Object[] lambdas = new Object[1]; public static void main(String[] args) throws Throwable { if (lambdas[0] == null) { ... lambdas[0] = LambdaMetafactory.metafactory0( "run", Runnable.class, samMethodType, implMethod); } Runnable r = (Runnable) lambdas[0]; new Thread(r).start(); } ... } 62
  • 63. CallSite public abstract class CallSite { public abstract MethodHandle getTarget(); ... } public class ConstantCallSite extends CallSite { public ConstantCallSite(MethodHandle target) {...} } 63
  • 64. Попытка 3: invokeDynamic public class Simple { private static CallSite[] callSites = new CallSite[1]; public static void main(String[] args) throws Throwable { if (callSites[0] == null) { MethodHandle implMethod = MethodHandles. lookup().findStatic( Simple. class, "lambda$main$0", MethodType.methodType(void.class)); MethodType samMethodType = MethodType. methodType(void.class); callSites[0] = LambdaMetafactory. metafactory( MethodHandles. lookup(), "run", MethodType.methodType(Runnable.class), samMethodType, implMethod, samMethodType); } Runnable r = (Runnable) callSites[0].getTarget().invoke(); ... } ... 64
  • 65. Попытка 3: invokeDynamic public class Simple { private static CallSite[] callSites = new CallSite[1]; public static void main(String[] args) throws Throwable { if (callSites[0] == null) { MethodHandle implMethod = MethodHandles. lookup().findStatic( Simple.class, "lambda$main$0", MethodType.methodType(void.class)); MethodType samMethodType = MethodType.methodType(void.class); callSites[0] = LambdaMetafactory. metafactory( MethodHandles. lookup(), "run", MethodType.methodType(Runnable.class), samMethodType, implMethod, samMethodType); } Runnable r = (Runnable) callSites[0].getTarget().invoke() ; ... } ... 65
  • 66. Попытка 3: реальная metafactory static CallSite metafactory(MethodHandles.Lookup caller, String invokedName, MethodType invokedType, MethodType samMethodType, MethodHandle implMethod, MethodType instantiatedMethodType) { Class cls = Unsafe.getUnsafe() .defineAnonymousClass(...); if (invokedType.parameterCount() == 0) { Object instance = cls.newInstance(); return new ConstantCallSite( MethodHandles.constant(samType, instance)); } ... } 66
  • 67. Замыкания (capturing lambdas) public class CapturingLambda { public void foo(String name) { Runnable r = () -> System.out.println("Hello, " + name); new Thread(r).start(); } } 67
  • 68. Замыкания: desugaring public class CapturingLambda { public void foo(String name) { Runnable r = () -> lambda$foo$0(name); new Thread(r).start(); } private static void lambda$foo$0(String name) { System.out.println("Hello, " + name); } } 68
  • 69. Замыкания: класс лямбды final class CapturingLambda$$Lambda$1 implements Runnable { private final String arg$1; private CapturingLambda$$Lambda$1(String s) { arg$1 = s; } private static Runnable get$Lambda(String s) { return new CapturingLambda$$Lambda$1(s); } public void run() { System.out.println("Hello, " + arg$1); } } 69
  • 70. Замыкания: кэширование в metafactory static CallSite metafactory(MethodHandles.Lookup caller, String invokedName, MethodType invokedType, MethodType samMethodType, MethodHandle implMethod, MethodType instantiatedMethodType) { Class cls = Unsafe.getUnsafe() .defineAnonymousClass(...); if (invokedType.parameterCount() == 0) {...} else { return new ConstantCallSite( MethodHandles.Lookup.IMPL_LOOKUP.findStatic(cls,"get$Lambda", invokedType)); } } 70
  • 71. Замыкания: invokeDynamic public class CapturingLambda { private static CallSite[] callSites = new CallSite[1]; public void foo(String name) throws Throwable { if (callSites[0] == null) { MethodHandle implMethod = MethodHandles. lookup().findStatic(CapturingLambda. class, "lambda$foo$0", MethodType.methodType(void.class, String.class)); MethodType samMethodType = MethodType. methodType(void.class); callSites[0] = LambdaMetafactory. metafactory(MethodHandles. lookup(), "run", MethodType.methodType(Runnable.class, String.class), samMethodType, implMethod, samMethodType); } Runnable r = (Runnable) callSites[0].getTarget().invoke(name); ... 71
  • 73. Утечки памяти нет public class Foo { void foo() { Runnable r =() -> System.out.println("Bye!"); Runtime.getRuntime() .addShutdownHook(new Thread(r)); } ... } 73
  • 74. Экземпляр для non-capturing переиспользуется ContainerUtil.filter(methods, method -> method.isConstructor(); ); ContainerUtil.filter(methods,PsiMethod::isConstructor) 74
  • 75. Это может приводить к проблемам public interface Disposable { void dispose(); } public class Disposer { public static void register(Disposable parent, Disposable child) { ... map.put(parent, child); } } 75
  • 76. Если лямбды сравниваются как объекты private final Disposable parent = new Disposable() { public void dispose() { } }; void foo() { Disposer.register(parent, ...); } private final Disposable parent = () -> { }; void foo() { Disposer.register(parent, ...); } 76
  • 77. Лямбды работают достаточно эффективно И создание, и вызовы хорошо инлайнятся. 77
  • 78. Ссылки Refactoring to Functional Style with Java 8 by Venkat Subramaniam Stream API, часть 1, Сергей Куксенко Stream API: Tagir Valeev Translation of Lambda Expressions by Brian Goetz Глубокое погружение в invokedynamic, Владимир Иванов 78
  • 79. Итоги ● используйте лямбды и стримы, чтобы писать более высокоуровневый и понятный код ● но не увлекайтесь, функциональный стиль – не самоцель, а лишь средство ● изучайте, как это устроено; особенно если натыкаетесь на непонятное поведение 79