c++返回指针时候注意提防
这对函数式编程并不会造成太大的影响,这真棒。 这是关于某些实践的警告,您很可能会将其应用于您的代码,而这是完全错误的! 。
高阶函数对于函数式编程是必不可少的,因此,谈论它们将帮助您成为聚会中的关注焦点。
如果您正在编写JavaScript,那么您就一直在这样做。 例如:
setTimeout(function() {alert('10 Seconds passed');
}, 10000);
上面的setTimeout()
函数是一个高阶函数。 它是一个使用匿名函数作为参数的函数。 10秒钟后,它将调用作为参数传递的函数。
我们可以编写另一个简单的高阶函数来提供上述功能:
var message = function(text) {return function() {alert(text);}
};setTimeout(message('10 Seconds passed'), 10000);
如果执行上述操作,则将执行message()
,并返回一个匿名函数,该函数将提醒您传递给message()
的参数文本
在函数式编程中,以上是常见的做法。 从高阶函数返回的函数将捕获外部作用域,并在调用时能够对此作用域。
为什么在Java中这种做法很危险?
出于同样的原因。 从高阶“函数”(方法)返回的“函数”(lambda)将捕获外部作用域,并在调用时能够对该作用域起作用。
这里给出了最简单的示例:
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()
调用的地方放置一个断点,则可以在堆栈上看到无害的lambda实例。 一个简单的生成类,支持功能接口的实现:
现在,让我们将此示例转换为您的普通Enterprise™应用程序(请注意注释 ),我们将其简化为适合此博客文章:
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");};}
}
断点仍在同一位置。 我们在堆栈上看到了什么?
仍然是一个无害的小lambda实例:
精细。 当然。 让我们添加一些其他日志记录,仅用于调试
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 hereSystem.out.println("Hello from: " + this);};}
}
哎呀!
突然, this
引用变得“无害”,迫使Java编译器将EnterpriseBean™
的封闭实例包含在返回的Runnable
类中:
随之而来的是沉重的enterpriseStateObject
,现在不再可以对其进行垃圾收集,直到调用站点释放无害的Runnable
小对象为止。
好吧,这现在不是什么新鲜事了吗?
确实不是。 Java 8没有一流的功能,没关系。 通过名义上的SAM类型支持lambda表达式的想法非常狡猾,因为它允许升级和lambda-y-fy Java生态系统中的所有现有库,而无需更改它们。
而且,在匿名课堂上,整个故事也就不足为奇了。 自从旧的Swing 1.0样式好的ActionListener
等以来,以下编码样式就已经通过匿名类泄漏了内部状态。
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() {@Overridepublic void run() {System.out.println("Hello from " + this);}};}
}
什么是新的? lambda样式将鼓励在各处使用Java中的高阶函数。 这通常是好的。 但是,只有当高阶函数是静态方法时,其结果类型才不会包含任何状态。
但是,通过上述示例,我们可以看到,在不久的将来,当我们开始接受Java 8的功能样式编程时,将通过一些内存泄漏和问题进行调试。
因此,请小心,并遵循以下规则:
(“纯”)高阶函数必须是Java中的静态方法!
进一步阅读
封闭实例之前已引起问题。 了解过去二十年来可怕的双花括号反模式如何在Java开发人员中引起痛苦和折磨。
翻译自: https://www.javacodegeeks.com/2015/11/beware-of-functional-programming-in-java.html
c++返回指针时候注意提防