Это не будет громкой болтовней о функциональном программировании, и это здорово. Это предупреждение о некоторых приемах, которые вы, вероятно, собираетесь применить к своему коду, которые ужасно неверны! ,
Функции высшего порядка необходимы для функционального программирования, и поэтому их обсуждение поможет вам быть в центре внимания на вечеринках.
Если вы пишете JavaScript, вы делаете это все время. Например:
|
1
2
3
|
setTimeout(function() { alert('10 Seconds passed');}, 10000); |
Вышеуказанная функция setTimeout() является функцией высшего порядка. Это функция, которая принимает анонимную функцию в качестве аргумента. Через 10 секунд он вызовет функцию, переданную в качестве аргумента.
Мы можем написать еще одну простую функцию более высокого порядка, которая в результате предоставляет вышеуказанную функцию:
|
1
2
3
4
5
6
7
|
var message = function(text) { return function() { alert(text); }};setTimeout(message('10 Seconds passed'), 10000); |
Если вы выполните вышеизложенное, message() будет выполняться, возвращая анонимную функцию, которая предупреждает текст аргумента, который вы передали message()
В функциональном программировании вышеуказанное является обычной практикой. Функция, возвращаемая из функции более высокого порядка, захватывает внешнюю область и может воздействовать на эту область при вызове.
Почему эта практика коварна на Яве?
По тем же причинам. «Функция» (лямбда-выражение), возвращаемая из «функции» (метода) более высокого порядка, будет захватывать внешнюю область и способна воздействовать на эту область при вызове.
Самый тривиальный пример приведен здесь:
|
01
02
03
04
05
06
07
08
09
10
11
12
|
class Test { public static void main(String[] args) { Runnable runnable = runnable(); runnable.run(); // Breakpoint here } static Runnable runnable() { return () -> { System.out.println("Hello"); }; }} |
В приведенной выше логике, если вы установите runnable.run() останова там, где выполняется runnable.run() , вы можете увидеть безвредный лямбда-экземпляр в стеке. Простой сгенерированный класс, поддерживающий реализацию функционального интерфейса:
Теперь давайте переведем этот пример в обычное приложение Enterprise ™ ( обратите внимание на аннотации ), которое мы значительно упростили, чтобы соответствовать этому сообщению в блоге:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
class Test { public static void main(String[] args) { Runnable runnable = new EnterpriseBean() .runnable(); runnable.run(); // Breakpoint here }}@ImportantDeclaration@NoMoreXML({ @CoolNewValidationStuff("Annotations"), @CoolNewValidationStuff("Rock")})class EnterpriseBean { Object[] enterpriseStateObject = new Object[100_000_000]; Runnable runnable() { return () -> { System.out.println("Hello"); }; }} |
Точка останова остается на том же месте. Что мы видим в стеке?
Все еще безобидный маленький экземпляр лямбды:
Хорошо. Конечно. Давайте добавим некоторые дополнительные журналы, просто для отладки
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
class Test { public static void main(String[] args) { Runnable runnable = new EnterpriseBean() .runnable(); runnable.run(); // Breakpoint here }}@ImportantDeclaration@NoMoreXML({ @CoolNewValidationStuff("Annotations"), @CoolNewValidationStuff("Rock")})class EnterpriseBean { Object[] enterpriseStateObject = new Object[100_000_000]; Runnable runnable() { return () -> { // Some harmless debugging here System.out.println("Hello from: " + this); }; }} |
По электронной почте Ой!
Внезапно «безобидная» небольшая this ссылка вынудила компилятор Java заключить включающий экземпляр EnterpriseBean™ в возвращенный класс Runnable :
И вместе с этим появился тяжелый enterpriseStateObject , который больше не может быть сборщиком мусора, пока сайт вызова не выпустит безобидный маленький Runnable
ОК, сейчас ничего нового, не так ли?
На самом деле это не так. Java 8 не имеет функций первого класса, и это нормально. Идея подкрепления лямбда-выражений номинальными типами SAM довольно хитрая, поскольку она позволяет обновлять и лямбда-и-фу все существующие библиотеки в экосистеме Java без их изменения.
Кроме того, с анонимным классом, вся эта история не была бы удивительной. Следующий стиль кодирования утек внутреннее состояние через анонимные классы со времен старого доброго стиля Swing 1.0 ActionListener et al.
|
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
|
class Test { public static void main(String[] args) { Runnable runnable = new EnterpriseBean() .runnable(); runnable.run(); }}@ImportantDeclaration@NoMoreXML({ @CoolNewValidationStuff("Annotations"), @CoolNewValidationStuff("Rock")})class EnterpriseBean { Object[] enterpriseStateObject = new Object[100_000_000]; Runnable runnable() { return new Runnable() { @Override public void run() { System.out.println("Hello from " + this); } }; }} |
Что нового? Лямбда-стиль будет поощрять использование повсюду функций высшего порядка в Java. Что в целом хорошо. Но только тогда, когда функция высшего порядка является статическим методом, чьи результирующие типы не будут заключать в себе никакого состояния.
Однако с помощью приведенных выше примеров мы можем увидеть, что в ближайшем будущем мы собираемся отладить пару утечек памяти и проблем, когда начнем программировать функциональный стиль Java 8.
Поэтому будьте осторожны и следуйте этому правилу:
(«Чистый») Функции высшего порядка ДОЛЖНЫ быть статическими методами в Java!
дальнейшее чтение
Прилагаемые экземпляры вызывали проблемы раньше. Прочитайте о том, как ужасный шаблон двойных фигурных скобок причинял боль и страдания Java-разработчикам в течение последних двух десятилетий.
| Ссылка: | Остерегайтесь функционального программирования на Java! от нашего партнера JCG Лукаса Эдера в блоге JAVA, SQL и JOOQ . |


