在Java中,当诸如log4J之类的框架在Java体系结构中变得流行时,经常会看到诸如以下代码:
if (logger.isEnabledFor(Logger.INFO)) {
// Ok to log now.
logger.info('ok' + 'to' + 'concatenate' + 'string' + 'to' + 'log' + 'message');
}
在执行任何String串联之前,始终检查是否为适当级别启用了日志记录是最佳实践。 我什至还记得十年前的一个项目(爱立信的3G无线电网络配置工具),该项目中用于记录日志的字符串连接实际上导致性能显着下降。
从那时起,JVM进行了优化,并且摩尔定律不断发展,因此String串联不再像以前那样令人担忧。 在许多框架(例如Hibernate )中,如果您检查源代码,则会看到日志记录代码,而无需检查是否启用了日志记录,并且无论如何都会进行字符串连接。 但是,让我们假装串联是一个性能问题。 我们真正想做的是消除对if
语句的需要,以停止代码膨胀。 这里的问题是,在Java中,当您调用带有参数的方法时,所有参数的值都是在调用该方法之前计算出来的。 这就是为什么需要if
语句的原因。
simpleComputation(expensiveComputation());// In Java, the expensive computation is called first.
logger.log(Level.INFO, 'Log this ' + message);// In Java, the String concatenation happens first
Scala提供了一种可以延迟参数评估的机制。 这称为“ 按名称呼叫” 。
def log(level: Level, message: => String) = if (logger.level.intValue >= level.intValue) logger.log(level, msg)
字符串类型前的=>
表示在调用日志函数之前不会评估字符串参数。 相反,将进行检查以确认记录器级别的值处于适当的值,如果是,则将评估字符串。 该检查在log
功能中进行,因此无需在每次调用前都放置该检查。 那代码重用呢?
还要别的吗?
是的,当使用传递名称时,传递名称的参数不仅会被评估一次,而且会在传递给函数的函数中每次被引用时进行评估。 让我们看另一个例子
scala> def nanoTime() = {
| println(">>nanoTime()")
| System.nanoTime // returns nanoTime
| }
nanoTime: ()Long
scala> def printTime(time: => Long) = { // => indicates a by name parameter
| println(">> printTime()")
| println("time= " + time)
| println("second time=" + time)
| println("third time=" + time)
| }
printTime: (time: => Long)Unit
scala> printTime(nanoTime())
>> printTime()
>>nanoTime()
time= 518263321668117
>>nanoTime()
second time=518263324003767
>>nanoTime()
third time=518263324624587
在这个例子中,我们可以看到nanoTime()不仅执行一次,而且每次在函数printTime
被引用时printTime
被执行。 这意味着它在此函数中执行了三次,因此我们得到了三个不同的时间。 直到下一次,保重身体。
参考: Scala:请以我的名字叫我? 从我们的JCG合作伙伴 Alex Staveley在都柏林的技术博客博客中获得。
翻译自: https://www.javacodegeeks.com/2013/01/scala-call-me-by-my-name-please.html