3.3 使用Clojure创建和编译Java类
Clojure的所有对象都实现了Java的特定接口:
1. Clojure数据结构实现了Java集合接口
2. Clojure函数实现了Runnable和Callable接口
Clojure可以在需要时很方便地在磁盘上生成一次性的代理或类,使用必要的一些Java代码片段。
创建Java代理
在Clojure中,可以使用代理来扩展一个类:
(proxy class-and-interfaces super-cons-args & fns)
示例:
(def print-element-handler
(proxy [DefaultHandler] []
(startElement
[uri local qname atts]
(println (format "Saw element: %s" qname)))))
说明:
1. proxy创建了代理类的一个实例;
2. proxy机制是完全通用的,可以用来任意Java对象
user=> (.start (Thread.
(proxy [Runnable] [] (run [] (println "I ran!")))))
user=> I ran!
3. 在Java中必须实现接口的所有方法,但在Clojure中不必如此,如
user=> (proxy [Callable] [])
#<Object$Callable clojure.proxy.java.lang.Object$Callable@11e0c13>
如果忽略方法实现,Clojure将提供一个默认实现,该实现抛出异常UnsupportedOperationException,如
user=> (.call (proxy [Callable] []))
java.lang.UnsupportedOperationException: call (NO_SOURCE_FILE:0)
Clojure的函数都自动实现Runable和Callable接口,下面举例说明:
; 正常用法:调用匿名函数
user=> (#(println "foo"))
; 调用run方法通过Runnable接口
user=> (.run #(println "foo"))
; 调用call方法通过Callable接口
user=> (.call #(println "foo"))
三种调用方法都输出foo
编译到硬盘
reader.tasklist类Clojure源代码:
(ns reader.tasklist
(:gen-class
:extends org.xml.sax.helpers.DefaultHandler
:state state
:init init)
(:use [clojure.contrib.duck-streams :only (reader)])
(:import [java.io File]
[org.xml.sax InputSource]
[org.xml.sax.helpers DefaultHandler]
[javax.xml.parsers SAXParserFactory]))
(defn -main [& args]
(dosseq [arg args]
(println (task-list arg))))
(defn task-list [arg]
(let [handler (new examples.tasklist)]
(.. SAXParserFactory newInstance newSAXParser
(parse (InputSource. (reader (File. arg)))
handler))
@(.state handler)))
(defn -init[]
[[] (atom [])])
(defn -startElement
[this uri local qname atts]
(when (= qname "target")
(swap! (.state this) conj (.getValue atts "name"))))
说明:
(1) :gen-class 指示Clojure生成Java类reader.tasklist
(2) :extends 指示生成类继承DefaultHandler(实现接口用:implements语句)
(3) Clojure生成Java类将类状态隔离到一个单独的状态结构,:state语句指定状态结构变量
(4) :init语句指定类初始化函数,初始化函数返回Java需要的基类构造函数参数和Clojure使用的初始状态结构
(5) Clojure生成reader.tasklist类,并创建方法;这些方法代理你提供的函数,每个方法代理一个相同名字的函数,方法名以连接符(-)为前缀
Clojure提供compile函数:
(compile lib)
compile函数对运行环境要求很苛刻:
(1) 要编译的lib必须从classpath可以访问
(2) 类文件生成目录(*complile-path*)必须在classpath中,默认为Clojure的启动目录
Clojure为类本身生成一个class文件,为类的每个函数或方法生成一个类,名字包含__init的类很特殊,它在第一次加载时执行lib的顶级代码。Clojure的这种模块性允许我们在运行时动态替换单个函数。
Clojure的所有对象都实现了Java的特定接口:
1. Clojure数据结构实现了Java集合接口
2. Clojure函数实现了Runnable和Callable接口
Clojure可以在需要时很方便地在磁盘上生成一次性的代理或类,使用必要的一些Java代码片段。
创建Java代理
在Clojure中,可以使用代理来扩展一个类:
(proxy class-and-interfaces super-cons-args & fns)
示例:
(def print-element-handler
(proxy [DefaultHandler] []
(startElement
[uri local qname atts]
(println (format "Saw element: %s" qname)))))
说明:
1. proxy创建了代理类的一个实例;
2. proxy机制是完全通用的,可以用来任意Java对象
user=> (.start (Thread.
(proxy [Runnable] [] (run [] (println "I ran!")))))
user=> I ran!
3. 在Java中必须实现接口的所有方法,但在Clojure中不必如此,如
user=> (proxy [Callable] [])
#<Object$Callable clojure.proxy.java.lang.Object$Callable@11e0c13>
如果忽略方法实现,Clojure将提供一个默认实现,该实现抛出异常UnsupportedOperationException,如
user=> (.call (proxy [Callable] []))
java.lang.UnsupportedOperationException: call (NO_SOURCE_FILE:0)
Clojure的函数都自动实现Runable和Callable接口,下面举例说明:
; 正常用法:调用匿名函数
user=> (#(println "foo"))
; 调用run方法通过Runnable接口
user=> (.run #(println "foo"))
; 调用call方法通过Callable接口
user=> (.call #(println "foo"))
三种调用方法都输出foo
编译到硬盘
reader.tasklist类Clojure源代码:
(ns reader.tasklist
(:gen-class
:extends org.xml.sax.helpers.DefaultHandler
:state state
:init init)
(:use [clojure.contrib.duck-streams :only (reader)])
(:import [java.io File]
[org.xml.sax InputSource]
[org.xml.sax.helpers DefaultHandler]
[javax.xml.parsers SAXParserFactory]))
(defn -main [& args]
(dosseq [arg args]
(println (task-list arg))))
(defn task-list [arg]
(let [handler (new examples.tasklist)]
(.. SAXParserFactory newInstance newSAXParser
(parse (InputSource. (reader (File. arg)))
handler))
@(.state handler)))
(defn -init[]
[[] (atom [])])
(defn -startElement
[this uri local qname atts]
(when (= qname "target")
(swap! (.state this) conj (.getValue atts "name"))))
说明:
(1) :gen-class 指示Clojure生成Java类reader.tasklist
(2) :extends 指示生成类继承DefaultHandler(实现接口用:implements语句)
(3) Clojure生成Java类将类状态隔离到一个单独的状态结构,:state语句指定状态结构变量
(4) :init语句指定类初始化函数,初始化函数返回Java需要的基类构造函数参数和Clojure使用的初始状态结构
(5) Clojure生成reader.tasklist类,并创建方法;这些方法代理你提供的函数,每个方法代理一个相同名字的函数,方法名以连接符(-)为前缀
Clojure提供compile函数:
(compile lib)
compile函数对运行环境要求很苛刻:
(1) 要编译的lib必须从classpath可以访问
(2) 类文件生成目录(*complile-path*)必须在classpath中,默认为Clojure的启动目录
Clojure为类本身生成一个class文件,为类的每个函数或方法生成一个类,名字包含__init的类很特殊,它在第一次加载时执行lib的顶级代码。Clojure的这种模块性允许我们在运行时动态替换单个函数。