Java8 和 Scala 中的高阶函数
函数文本最大的优势就是可以像字符串或者对象等其他文本(literal)一样传送它。这种特性为构建高度紧凑和可重用代码提供无限的可能性。
1、我们的第一个高阶函数
当我们将一个函数文本传送给一个方法的时候,我们最主要的是一个接收方法参数的方法(这个确实很绕-_-|||),这类方法就叫做高阶函数。上文Swing例子中提到的 addActionListener 方法恰好属于这类。我们还可以定义自己的高阶函数来为自己提供许多便利。让我们看一个简单的例子:
def measure[T](func:=>T):T { val start = System.nanoTime() val result = func val elapsed = System.nanoTime() - start; print("The execution of this call took: %s ns".format(elapsed)); result }
在这个例子中,我们声明了一个名为measure的方法用来计算这个名为func的函数文本的回调所需要的时间。func 方法的签名(signature)是它不接收任何参数并且返回一个泛型类型T。正如你所看到的,Scala中的函数并不一定需要参数尽管它们能够——而且往往也含有参数。
现在我们可以向 measure 方法中传递任何函数文本(或者方法)。
def myCallback = { Thread.sleep(1000) "I just took a poewrnap" } val result = measure(myCallback); > The execution of this call took: 100244900 ns
从概念的角度讲我们所做的,就是将计算方法调用时间和实际的运算区分开来。我们构造了两块可以重用、松散耦合、类似于拦截器(interceptor)的代码块(measure和myCallback)
2、通过高阶函数实现重用
先看一个假设的例子,两个可重用构造略紧耦合:
def doWithContact(fileName:String, handle:Contact => Unit):Unit = { try { val contactStr = io.Source.fromFile(fileName).mkString val contact = AContactParser.parse(contactStr) handle(contact) } catch { case e: IOException => println("couldn't load contact file: " + e); case e: ParseException => println("coulnd't parse contact file: " + e); } }
doWithContact 方法从文件中读取电子名片之类的联系方式然后将数据提供给一个解析器(parser)将数据转化成为联系领域的对象。然后这个对象被传递给一个函数文本回调 handle。 doWithContact 方法 很函数文本均返回 Unit 类型,等同于java中的返回void的方法。
现在,我们可以定义各种各样的可以传递给 doWithContact 的回调函数:
val storeCallback = (c:Contact) => ContactDao.save(c) val sendCallback = (c:Contact) => { val msgBody = AConverter.convert(c) RestService.send(msgBody) } val combineCallback = (c:Contact) => { storeCallback(c) sendCallback(c) } doWithContact("custerX.vcf", storeCallback) doWithContact("custerY.vcf", sendCallback) doWithContact("custerZ.vcf", combineCallback) doWithContact("custerZ.vcf", combineCallback)
回调函数也可以通过内联传递:
doWithContact("custerW.vcf", (c:Contact) => ContactDao.save(c))
3、Java 8 中的高阶函数
java 8 中的等效实现看起来十分相似——使用目前的语法建议:
public interface Block<T> { void apply(T t); } public void doWithContact(String fileName, Block<Contact> block) { try { String contacStr = FileUtils.readFileToString(new File(fileName)); Contact.apply(contact); block.apply(contact); } catch (IOException e) { System.out.println("cloudn't load contact file: " + e.getMessage()); } catch (ParseException e) { System.out.println("cloudn't parse contact file: " + e.getMessage()); } } //usage doWithContact("custerX.vcf", c -> ContactDao.save(c))
4、使用高阶函数的益处
正如你见到的,函数帮助我们干净地将对象的创建和处理区分开来。通过这种方法,新的业务逻辑处理对象就可以轻易的添加进来而没有必要同对象创建逻辑相耦合。