Clojure与Java交互:从创建类到实际应用
立即解锁
发布时间: 2025-08-19 02:29:24 阅读量: 1 订阅数: 4 


探索Clojure编程:从入门到精通
### Clojure与Java交互:从创建类到实际应用
#### 1. 在Clojure中创建Java类
Clojure的对象能够实现合理的Java接口,其数据结构实现了Java集合API中的接口,函数则实现了`Runnable`和`Callable`接口。除了这些通用接口,有时还需要特定领域的接口,比如事件驱动API(如Swing或某些XML解析器)的回调处理程序。Clojure可以轻松地按需生成一次性代理或类,所需代码量远少于Java。
##### 1.1 创建Java代理
为了与Java进行交互,常常需要实现Java接口。以使用简单API for XML(SAX)解析器解析XML为例,首先导入必要的类:
```clojure
(import '(org.xml.sax InputSource)
'(org.xml.sax.helpers DefaultHandler)
'(java.io StringReader)
'(javax.xml.parsers SAXParserFactory))
```
使用SAX解析器需要实现回调机制,通常可以通过扩展`DefaultHandler`类来实现。在Clojure中,可以使用`proxy`函数扩展类:
```clojure
(proxy class-and-interfaces super-cons-args & fns)
```
以下是一个简单的示例,使用`proxy`创建一个`DefaultHandler`,用于打印所有对`startElement`的调用细节:
```clojure
(def print-element-handler
(proxy [DefaultHandler] []
(startElement [uri local qname atts]
(println (format "Saw element: %s" qname)))))
```
`proxy`生成一个代理类的实例。第一个参数`[DefaultHandler]`是超类和超接口的向量,第二个参数`[]`是基类构造函数的参数向量,这里不需要参数。在代理设置之后是零个或多个代理方法的实现代码。上述代理有一个名为`startElement`的方法,它接受四个参数并打印`qname`参数的名称。
接下来,需要一个解析器来传递处理程序。可以创建一个函数来解析字符串中的XML:
```clojure
(defn demo-sax-parse [source handler]
(.. SAXParserFactory newInstance newSAXParser
(parse (InputSource. (StringReader. source)) handler)))
```
现在解析变得很简单:
```clojure
(demo-sax-parse "<foo>
<bar>Body of bar</bar>
</foo>" print-element-handler)
```
输出结果:
```
| Saw element: foo
| Saw element: bar
```
这个示例展示了如何创建Clojure代理来处理Java的XML接口。也可以采用类似的方法实现自定义的Java接口。不过,如果只是进行XML处理,`clojure.data.xml`库已经提供了出色的XML支持,并且可以与任何SAX兼容的Java解析器一起使用。
代理机制非常通用,可以动态生成任何类型的Java对象。有时对象非常简单,可以在一行代码中完成:
```clojure
(.start (Thread.
(proxy [Runnable] [] (run [] (println "I ran!")))))
```
在Java中,实现每个接口时必须提供每个方法的实现。而在Clojure中,可以省略某些方法的实现,Clojure会提供一个默认实现,抛出`UnsupportedOperationException`:
```clojure
(proxy [Callable] []) ; 无方法的代理
(.call (proxy [Callable] [])) ; 抛出异常
```
对于只有一个方法的接口(如`Runnable`和`Callable`),默认实现意义不大,但在实现较大的接口且不需要某些方法时会很方便。
实际上,Clojure函数会自动实现`Runnable`和`Callable`接口,这使得将Clojure函数传递给其他线程变得非常容易:
```clojure
(dotimes [i 5]
(.start
(Thread.
(fn []
(Thread/sleep (rand 500))
(println (format "Finished %d on %s" i (Thread/currentThread)))))))
```
对于一次性任务(如XML和线程回调),Clojure的代理使用起来快速简便。如果需要更持久的类,也可以从Clojure生成新的命名类。
##### 1.2 使用Java集合
大多数情况下,Clojure的集合可以替代Java集合。Clojure集合具有并发安全性、良好的性能特征,并实现了适当的Java集合接口。因此,在Clojure中工作时,通常应优先使用Clojure自己的集合,甚至在方便时将它们传递回Java。
如果选择使用Java集合,Clojure不会阻止。从Clojure的角度来看,Java集合与其他类一样,各种Java互操作形式都可以正常工作。但Java集合是为基于锁的并发设计的,它们不能提供Clojure集合的并发保证,并且与Clojure的软件事务内存配合不佳。
在处理Java数组时,需要特别注意。在Java中,数组有自己的语法和字节码指令,不实现任何Java接口,Clojure集合不能伪装成数组。Clojure提供了`make-array`函数来创建Java数组:
```clojure
(make-array class length)
(make-array class dim & more-dims)
```
例如,创建一个一维字符串数组:
```clojure
(make-array String 5)
```
输出结果可能类似于`#<String[] [Ljava.lang.String;@45a270b2>`,这是Java对数组的`toString()`实现。为了在REPL中打印数组的各个条目,可以使用Clojure的`seq`函数将任何Java数组包装为Clojure序列:
```clojure
(seq (make-array String 5)) ; 输出 (nil nil nil nil nil)
```
Clojure还包括一系列用于创建Java基本类型数组的函数,如`int-array`。可以在REPL中使用以下命令查看这些和其他数组函数的文档:
```clojure
(find-doc "-array")
```
Clojure提供了一组对Java数组的低级操作,包括`aset`、`aget`和`alength`:
```clojure
(aset java-array index value)
(aset java-array index-dim1 index-dim2 ... value)
(aget java-array index)
(aget java-array index-dim1 index-dim2 ...)
(alength java-array)
```
以下是一个示例,展示如何使用这些操作:
```clojure
(defn painstakingly-create-array []
(let [arr (make-array String 5)]
(aset arr 0 "Painstaking")
(aset arr 1 "to")
(aset arr 2 "fill")
(aset arr 3 "in")
(aset arr 4 "arrays")
arr))
(aget (painstakingly-create-array) 0) ; 输出 "Painstaking"
(alength (painstakingly-create-array)) ; 输出 5
```
大多数情况下,使用更高级的函数(如`to-array`)会更简单,它可以直接从任何集合创建数组:
```clojure
(to-array sequence)
```
`to-array`总是创建一个`Object`数组:
```clojure
(to-array ["Easier" "array" "creation"]) ; 输出 #<Object[] [Ljava.lang.Object;@1639f9e3>
```
`to-array`在调用接受可变参数列表的Java方法(如`String/format`)时也很有用:
```clojure
(String/format "Training Week: %s Mileage: %d"
(to-array [2 26])) ; 输出 "Training Week: 2 Mileage: 26"
```
`to-array`的“表亲”`into-array`可以创建比`Object`更具体类型的数组:
```clojure
(into-array type? seq)
```
可以将显式类型作为可选的第一个参数传递给`into-array`:
```clojure
(into-array String ["Easier", "
```
0
0
复制全文
相关推荐










