2017-10-10 62 views
3

Clojure有相当于Java的try-with-resources构造吗?尝试在Clojure中使用资源

如果不是,那么在Clojure代码中处理这种习惯用法的正常方法是什么?

用于安全打开和关闭资源的Java-7以前的习惯用法是非常详细的,它们实际上增加了对语言尝试资源的支持。对我来说,在标准的Clojure库中我找不到这个用例的宏是很奇怪的。

基于Clojure的主流项目存储库的一个例子 - 显示如何在实践中处理这个问题 - 将是非常有用的。

回答

6

您可以使用with-open将资源绑定到符号,并确保一旦控制流离开块,资源就会关闭。

以下示例来自clojuredocs。

(with-open [r (clojure.java.io/input-stream "myfile.txt")] 
    (loop [c (.read r)] 
     (when (not= c -1) 
     (print (char c)) 
     (recur (.read r))))) 

这将扩大到以下几点:

(let [r (clojure.java.io/input-stream "myfile.txt")] 
    (try 
    (loop [c (.read r)] 
     (when (not= c -1) 
     (print (char c)) 
     (recur (.read r)))) 
    (finally (.close r)))) 

你可以看到一个let块与try创建 - finally的号召.close()方法。

+0

好的,这是有道理的。我仍然必须在'with-open'周围添加另一个'try'形式来捕获在打开或使用资源时抛出的任何异常 - 但是我猜想独立于try/catch逻辑处理开放/关闭自动化不是一件坏事。我只是期望与Java构造有1:1的等价关系,但我真的没有理由期待这一点。 – DaoWen

+0

你可以在一个'(try ...)'块中有一个'(catch Exception ...)'和一个'(finally(.close r))',但是'with-resource'宏不支持它默认,所以你将不得不自己写。 – erdos

+0

@DaoWen它是1:1等价。 'try(Foo x = bar()){...}'不处理调用'bar()'时遇到的任何异常。它怎么可能?在作用域中没有有效的'x'来调用'.close()'。 – amalloy

0

你可以做些更接近java的东西,在with-open之上构成一些宏。它可能看起来像这样:

(defmacro with-open+ [[var-name resource & clauses] & body] 
    (if (seq clauses) 
    `(try (with-open [~var-name ~resource] [email protected]) 
      [email protected]) 
    `(with-open [~var-name ~resource] [email protected]))) 

因此您可以将附加的子句一起传递给绑定。

(with-open+ [x 111] 
    (println "body")) 

扩展到简单with-open

(let* 
    [x 111] 
    (try (do (println "body")) (finally (. x clojure.core/close)))) 

,同时附加条款导致的try-catch包裹它:

(with-open+ [x 111 
      (catch RuntimeException ex (println ex)) 
      (finally (println "finally!"))] 
    (println "body")) 

扩展到

(try 
    (let* 
    [x 111] 
    (try (do (println "body")) (finally (. x clojure.core/close)))) 
    (catch RuntimeException ex (println ex)) 
    (finally (println "finally!"))) 

但仍这是相当意见ed解决方案。