在处理数据库连接或者输入输出流等场景时,我们经常需要写一些非常繁琐又枯燥乏味的代码来关闭数据库连接或输入输出流。

例如数据库操作:

  def update(sql: String)(conn: Connection): Int = {
var statement: Statement = null
try {
statement = conn.createStatement()
statement.executeUpdate(sql)
} finally {
if (conn != null) conn.close()
if (statement != null) statement.close()
}
}

例如文本操作:

  def save(sql: String, path: String): Unit = {
var pw: PrintWriter = null
try {
pw = new PrintWriter(path)
pw.println(sql)
} finally {
if (pw != null) pw.close()
}
}

从上面的两个例子中(这样的例子还有很多)可以发现两者存在相同模式的代码:

var a: A = null
try {
a = xxx
//对a进行操作
} finally {
if(a != null) a.close()
}

那能不能把这些相同模式的代码抽象出来呢?答案是肯定的,我们可以利用Scala的泛型和高阶函数来完成。先写一个高阶函数:

  def using[A <: { def close(): Unit }, B](a: A)(f: A => B): B = {
try f(a)
finally {
if(a != null) a.close()
}
}

using函数有两个参数a: A、f: A => B,其中a是需要关闭的资源,f是一个输入为A输出为B的函数。现在我们可以利用using函数来重写数据库操作和文本操作了。

数据库操作:

  def update0(sql: String)(conn: Connection): Int = {
using(conn) {
conn => {
using(conn.createStatement()) {
statement => statement.executeUpdate(sql)
}
}
}
}

文本操作:

  def save0(sql: String, path: String): Unit = {
using(new PrintWriter(path)) {
pw => pw.println(sql)
}
}

可以看出重写后的函数相比之前更加精炼,函数只需要关心实现自己的逻辑而不用关心资源关闭操作,这些就交给using函数来处理吧。目前看来using函数似乎可以满足我们的需求了,真的是这样吗?我们再看一个例子:

  def query[A](sql: String, conn: Connection)(implicit f: ResultSet => List[A]): List[A] = {
using(conn) {
conn => {
using(conn.createStatement()) {
statement => {
using(statement.executeQuery(sql)){
resultSet => f(resultSet)
}
}
}
}
}
}

可以看到上面的例子用到了3次using,嵌套了3层函数,代码的可读性变差。而且一旦需要关闭的资源变多,嵌套函数的层数也将相应增加。代码又陷入另一个繁琐枯燥的模式。有什么更好的办法吗?也许可以试一下for表达式这个语法糖,那么代码将如下表示:

  def query0[A](sql: String, conn: Connection)(implicit f: ResultSet => List[A]): List[A] = {
for {
conn <- Closable(conn)
stmt <- Closable(conn.createStatement())
rs <- Closable(stmt.executeQuery(sql))
} yield f(rs)
}

这样没有了复杂的嵌套函数,代码的可读性更好了。可是Closable是什么类型?别急,我们可以从上面的代码分析出Closable类型是什么结构,首先Closable类型有一个可关闭资源的属性;然后Closable类型可以使用for表达式语法糖,那么Closable类型需要实现map和flatMap函数。Closable类型实现如下:

case class Closable[A <: { def close(): Unit }](a: A) {

  def map[B](f: A => B): B =
try f(a)
finally {
if(a != null) a.close()
} def flatMap[B](f: A => B): B = map(f) }

到此代码已经满足我们的需求了,但是还是很想探究其中的魔法。我们知道for表达式其实就是flatMap加map的语法糖,那么让我们剥开糖衣看看这块糖真实的模样:

  def query1[A](sql: String, conn: Connection)(implicit f: ResultSet => List[A]): List[A] = {
Closable(conn).flatMap {
conn => Closable(conn.createStatement()).flatMap {
stmt => Closable(stmt.executeQuery(sql)).map {
rs => f(rs)
}
}
}
}

让我们接着剥开flatMap

  def query2[A](sql: String, conn: Connection)(implicit f: ResultSet => List[A]): List[A] = {
Closable(conn).map {
conn => Closable(conn.createStatement()).map {
stmt => Closable(stmt.executeQuery(sql)).map {
rs => f(rs)
}
}
}
}

最后剥开map

  def query3[A](sql: String, conn: Connection)(implicit f: ResultSet => List[A]): List[A] = {
Closable(conn).map {
conn => try {
Closable(conn.createStatement()).map {
stmt => try {
Closable(stmt.executeQuery(sql)).map {
rs => try {
f(rs)
} finally {
if(rs != null) rs.close()
}
}
} finally {
if (stmt != null) stmt.close()
}
}
} finally {
if (conn != null) conn.close()
}
}
}

到此Closable类型的神秘面纱已经完全揭开,希望Closable类型可以在各位读者工作中在处理一些需要关闭资源的时候提供一种选择,最后再多说两句。有时我们只需要处理两个可关闭资源,而且这两个资源之间没有关联。例如文本操作有一个输入流一个输出流,那么我们使用Closable类型代码将会如下:

  def save1(inPath: String, outPath: String): Unit = {
for {
in <- Closable(new BufferedReader(new FileReader(inPath)))
out <- Closable(new PrintWriter(outPath))
} yield out.println(in.readLine())
}

不是说上面的代码不好,而是我们可以做到更加简练,代码如下:

  def save2(inPath: String, outPath: String): Unit = {
Closable(new BufferedReader(new FileReader(inPath)))
.map2(new PrintWriter(outPath)){ (in, out) => out.println(in.readLine()) }
}

让我们来看下 map2的实现:

  def map2[B <: { def close(): Unit }, C](b: B)(f: (A, B) => C): C =
for {
ac <- Closable(a)
bc <- Closable(b)
} yield f(ac, bc)

聪明的读者可能还会有疑惑,map2用于关闭两个资源,那关闭3个资源我们需要实现一个map3,关闭N个资源岂不是要实现一个mapN。当然我们可以使用for表达式实现,但是还有更好的实现吗?哈哈,这个就交给读者课后思考了,相信聪明的你一定有自己的想法。

Scala实现Try with resources自动关闭IO的更多相关文章

  1. Java使用Try with resources自动关闭资源

    Try-with-resources Try-with-resources是Java7中一个新的异常处理机制,它能够很容易地关闭在try-catch语句块中使用的资源. 利用Try-Catch-Fin ...

  2. 自动关闭IO流-jdk1.7版本

    public static void main(String[] args) throws IOException { try( FileInputStream fis = new FileInput ...

  3. Java注解(二):实战 - 直接使用对象列表生成报表

    通过对Java注解(一):介绍,思想及优点学习了解,相信大家对Java注解有一定程度的了解,本篇文章将实战项目中的应用来加深对Java注解的了解. 本实例实现根据指定字段的JavaBean,生成对应列 ...

  4. scala 随笔

    创建map,并向map添加元素 val idMap = Map( "group_id" -> "GID", "sim_id" -> ...

  5. Scala学习笔记之二--基本数据类型

    前言 本篇主要讲Scala的基本数据类型,更多教程请参考:Scala教程 基本数据类型 Scala一共提供了9中数据类型,Scala的基本数据类型与java中的基本数据类型是一一对应的,这是Scala ...

  6. 转载:scala中:: , +:, :+, :::, ++的区别

    原文链接:https://segmentfault.com/a/1190000005083578 初学Scala的人都会被Seq的各种操作符所confuse.下面简单列举一下各个Seq操作符的区别. ...

  7. Spark集群 + Akka + Kafka + Scala 开发(1) : 配置开发环境

    目标 配置一个spark standalone集群 + akka + kafka + scala的开发环境. 创建一个基于spark的scala工程,并在spark standalone的集群环境中运 ...

  8. A quick tour of JSON libraries in Scala

    A quick tour of JSON libraries in Scala Update (18.11.2015): added spray-json-shapeless libraryUpdat ...

  9. 在Javaweb中使用Scala

    Java 是一门比较优秀的编程语言, 其最大功劳是建立非常繁荣的JVM平台生态.不过 Java 语法比较麻烦,写过 C, Python 的人总是想使用简洁的语法,又希望利用上 Java 平台的强大,因 ...

随机推荐

  1. 解决WordPress用户名密码都正确但点击登陆就清空密码的问题

    作者:荒原之梦 问题产生的环境与问题描述: 今天我在浏览器的书签里将我保存的用于登陆WordPress的书签名字改了一下,之后再登陆WordPress就出现提示说Cookies被阻止,要启用Cooki ...

  2. InnoDB存储引擎结构介绍

    Ⅰ.InnoDB发展史 时间 事件 备注 1995 由Heikki Tuuri创建Innobase Oy公司,开发InnoDB存储引擎 Innobase开始做的是数据库,希望卖掉该公司 1996 My ...

  3. 绕过token

    网站搭好了,下一步的目标就是直奔后台.因为一般前端在未登录的情况下只有查的功能.咱们的目标是增删改. 看到有添加功能时,先别着急的直接黑盒测试.先看看有没有防护 ######## 查看源码,搜索tok ...

  4. vue 实践记录

    打包后使用相对路径 在 build/webpack.prod.conf.js 的 output 节点添加配置:publicPath: './' 打包时使用shell复制文件 在入口 build/bui ...

  5. ThreadPoolExecutor简介

    ThreadPoolExecutor简介 并发包中提供的一个线程池服务 23456789 public ThreadPoolExecutor(int corePoolSize,//线程池维护线程的最少 ...

  6. BZOJ_1975_[Sdoi2010]魔法猪学院_A*

    BZOJ_1975_[Sdoi2010]魔法猪学院_A* Description iPig在假期来到了传说中的魔法猪学院,开始为期两个月的魔法猪训练.经过了一周理论知识和一周基本魔法的学习之后,iPi ...

  7. BZOJ_4530_[Bjoi2014]大融合_LCT

    BZOJ_4530_[Bjoi2014]大融合_LCT Description 小强要在N个孤立的星球上建立起一套通信系统.这套通信系统就是连接N个点的一个树. 这个树的边是一条一条添加上去的.在某个 ...

  8. BZOJ_2743_[HEOI2012]采花_离线+树状数组

    BZOJ_2743_[HEOI2012]采花_离线+树状数组 Description 萧芸斓是Z国的公主,平时的一大爱好是采花.今天天气晴朗,阳光明媚,公主清晨便去了皇宫中新建的花园采花 .花园足够大 ...

  9. BZOJ_3132_上帝造题的七分钟_树状数组

    BZOJ_3132_上帝造题的七分钟_树状数组 Description “第一分钟,X说,要有矩阵,于是便有了一个里面写满了0的n×m矩阵. 第二分钟,L说,要能修改,于是便有了将左上角为(a,b), ...

  10. 深入css布局篇(1) — 盒模型 & 元素分类

    深入css布局(1)-- 盒模型 & 元素分类     " 在css知识体系中,除了css选择器,样式属性等基础知识外,css布局相关的知识才是css比较核心和重要的点.今天我们来深 ...