2010-03-07 139 views
25

我定义“使用”功能如下:斯卡拉:“使用”功能

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

我可以用它这样的:

using(new PrintWriter("sample.txt")){ out => 
    out.println("hellow world!") 
} 

现在我很好奇如何定义“使用'功能采取任意数量的参数,并能够分别访问它们:

using(new BufferedReader(new FileReader("in.txt")), new PrintWriter("out.txt")){ (in, out) => 
    out.println(in.readLIne) 
} 
+1

重复:http://stackoverflow.com/questions/2207425/what-automatic-resource-management-alternatives-exists- for-scala – retronym

+1

在'try'内部执行'closeable.close()',或者你可以在'f(可关闭)'中屏蔽异常。 –

+0

相关http://stackoverflow.com/q/7602804/243233 – Jus12

回答

2

不幸的是,不支持任意长度的pa rameter使用标准Scala中的任意类型列表。

您可能可以通过一些语言更改来做类似的事情(以便将可变参数列表作为HList传递;请参阅here约需要的1/3)。

现在,最好的办法就是做元组和函数做的事情:用N来实现尽可能多的N。

二是很容易的,当然:

def using2[A, B <: {def close(): Unit}, C <: { def close(): Unit}](closeB: B, closeC: C)(f: (B,C) => A): A = { 
    try { f(closeB,closeC) } finally { closeB.close(); closeC.close() } 
} 

如果你需要更多的,它可能是值得写的东西,会生成源代码。

+0

两个在'finally'不安全的地方关闭。 – zinking

6

我一直在想这个,我想可能是有另一种方式来解决这个问题。下面是我对支持的参数“任意数量”(由什么元组提供有限):

object UsingTest { 

    type Closeable = {def close():Unit } 

    final class CloseAfter[A<:Product](val x: A) { 
    def closeAfter[B](block: A=>B): B = { 
     try { 
     block(x); 
     } finally { 
     for (i <- 0 until x.productArity) { 
      x.productElement(i) match { 
      case c:Closeable => println("closing " + c); c.close() 
      case _ => 
      } 
     } 
     } 
    } 
    } 

    implicit def any2CloseAfter[A<:Product](x: A): CloseAfter[A] = 
    new CloseAfter(x) 

    def main(args:Array[String]): Unit = { 
    import java.io._ 

    (new BufferedReader(new FileReader("in.txt")), 
    new PrintWriter("out.txt"), 
    new PrintWriter("sample.txt")) closeAfter {case (in, out, other) => 
     out.println(in.readLine) 
     other.println("hello world!") 
    } 
    } 
} 

我想我重复使用22元组/产品类别已经写在库中的事实......我不认为这个语法比使用嵌套using(没有双关意图)更清晰,但它是一个有趣的谜题。

edit:按照retronym的建议,用case (in, out, other)替换val赋值。

+1

这是一个很好的窍门,它可惜你在元组内容中牺牲了类型安全性。如果你用'case(in,out,other)=>'来代替'x => val(in,out,other)= x',那么闭包就可以更清洁了 - >' – retronym

+0

谢谢!我想知道是否有什么比val的任务更好。我会更新答案。 – huynhjl

2

这里是一个例子,它允许你使用scala作为自动资源管理块来理解任何java.io.Closeable的项目,但是它可以很容易地扩展为适用于任何接近方法。

该用法看起来非常接近using语句,并允许您轻松地在一个块中定义尽可能多的资源。

object ResourceTest{ 
    import CloseableResource._ 
    import java.io._ 

    def test(){ 
    for(input <- new BufferedReader(new FileReader("/tmp/input.txt")); output <- new FileWriter("/tmp/output.txt")){ 
     output.write(input.readLine) 
    } 
    } 
} 

class CloseableResource[T](resource: =>T,onClose: T=>Unit){ 
    def foreach(f: T=>Unit){ 
    val r = resource 
    try{ 
     f(r) 
    } 
    finally{ 
     try{ 
     onClose(r) 
     } 
     catch{ 
     case e => 
      println("error closing resource") 
      e.printStackTrace 
     } 
    } 
    } 
} 

object CloseableResource{ 
    implicit def javaCloseableToCloseableResource[T <: java.io.Closeable](resource:T):CloseableResource[T] = new CloseableResource[T](resource,{_.close}) 
} 
10

有人已经做到了这一点—这就是所谓的Scala ARM

自述:

import resource._ 
for(input <- managed(new FileInputStream("test.txt")) { 
    // Code that uses the input as a FileInputStream 
} 
1

这种解决方案并不相当有你想要的语法,但我认为这是足够接近:)

def using[A <: {def close(): Unit}, B](resources: List[A])(f: List[A] => B): B = 
    try f(resources) finally resources.foreach(_.close()) 

using(List(new BufferedReader(new FileReader("in.txt")), new PrintWriter("out.txt"))) { 
    case List(in: BufferedReader, out: PrintWriter) => out.println(in.readLine()) 
} 

当然不好的一面是您必须在使用块中输入BufferedReaderPrintWrter的类型。您可以添加一些魔法,以便您只需要List(in, out),使用类型A时使用multiple ORed type bounds即可。

通过定义一些非常冒险和危险的隐式转换,您可以避免必须键入List(以及另一种避开指定资源类型的方法),但是我没有记录详细信息,因为它太危险了。

0

从程序路径中分离清理算法是一个好主意。

该解决方案可让您累积范围内的可关闭数据。
范围清理将在块执行后发生,或者范围可以分离。随后可以对范围进行清洁。

通过这种方式,我们可以获得与单线程编程相同的便利性。

实用类:

import java.io.Closeable 

object ManagedScope { 
    val scope=new ThreadLocal[Scope](); 
    def managedScope[T](inner: =>T):T={ 
    val previous=scope.get(); 
    val thisScope=new Scope(); 
    scope.set(thisScope); 
    try{ 
     inner 
    } finally { 
     scope.set(previous); 
     if(!thisScope.detatched) thisScope.close(); 
    } 
    } 

    def closeLater[T <: Closeable](what:T): T = { 
    val theScope=scope.get(); 
    if(!(theScope eq null)){ 
     theScope.closeables=theScope.closeables.:+(what); 
    } 
    what; 
    } 

    def detatchScope(): Scope={ 
    val theScope=scope.get(); 
    if(theScope eq null) null; 
    else { 
     theScope.detatched=true; 
     theScope; 
    } 
    } 
} 

class Scope{ 
    var detatched=false; 
    var closeables:List[Closeable]=List(); 

    def close():Unit={ 
    for(c<-closeables){ 
     try{ 
     if(!(c eq null))c.close(); 
     } catch{ 
     case e:Throwable=>{}; 
     } 
    } 
    } 
} 

用法:

def checkSocketConnect(host:String, portNumber:Int):Unit = managedScope { 
    // The close later function tags the closeable to be closed later 
    val socket = closeLater(new Socket(host, portNumber)); 
    doWork(socket); 
    } 

    def checkFutureConnect(host:String, portNumber:Int):Unit = managedScope { 
    // The close later function tags the closeable to be closed later 
    val socket = closeLater(new Socket(host, portNumber)); 
    val future:Future[Boolean]=doAsyncWork(socket); 

    // Detatch the scope and use it in the future. 
    val scope=detatchScope(); 
    future.onComplete(v=>scope.close()); 
    }