C#里面有一个常用的关键字using,它可以保证资源在使用完毕后被正常关闭,使得程序员不必手动的关闭资源.
use using:1
2
3
4using (Font font1 = new Font("Arial", 10.0f))
{
byte charset = font1.GdiCharSet;
}
without using:1
2
3
4
5
6
7
8
9
10
11
12{
Font font1 = new Font("Arial", 10.0f);
try
{
byte charset = font1.GdiCharSet;
}
finally
{
if (font1 != null)
((IDisposable)font1).Dispose();
}
}
java 7以后java里也有了类似的特性。如果一个类实现了java.lang.AutoCloseable,那么你可以使用如下的语法。1
2
3
4try(ClassImplementingAutoCloseable obj = new ClassImplementingAutoCloseable())
{
...
}
接下来介绍如果一个类没有实现AutoCloseable,而又想实现类似using的功能应该怎么做。
MyFile是一个在使用之后需要被关闭的资源(需要调用close方法)。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17class MyFile {
private final String fileName;
MyFile(String fileName) {
super();
this.fileName = fileName;
}
void close() {
System.out.println("closing file:" + fileName);
}
void print() {
System.out.println("fileName:" + fileName);
}
}
MyFileHelper是MyFile的一个工具类。
currier接收第一个参数myFile,返回一个Consumer<Consumer<MyFile>>
。
意思是这个consumer接收另外一个consumer作为参数(另外这个consumer才会真正的消费myFile)。
在currier的第二个参数被应用之前,它是不知道myFile会如何被消费的。
当然你也可以把consumer换成function,资源消费之后再产生一个结果。
这里用到了Currying,Currying是在函数式语言中接收单参数的函数模拟多参数的函数的技术。
使用Currying让它在语法上它更接近using,把资源的初始化和资源的处理分开。
同时,consumer和myfile两个参数能够在不同的地方被确定,它们可以被互相隔离的测试。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15class MyFileHelper {
static Consumer<Consumer<MyFile>> using(MyFile file) {
Function<MyFile, Consumer<Consumer<MyFile>>> currier = myFile -> consumer -> {
try {
consumer.accept(myFile);
} finally {
myFile.close();
}
};
Consumer<Consumer<MyFile>> curried = currier.apply(file);
return curried;
}
}
}
最后是测试代码。1
2
3
4
5public static void main(String[] args) {
MyFileHelper.using(new MyFile("my-file")).accept(
myFile -> myFile.print());
}
}
输出:1
2fileName:my-file
closing file:my-file
完整的代码可以在这里看到:
gist