clojure是基于jvm的语言,就是说clojure会被编译成字节码被jvm执行。但是clojure能做的可不仅仅是被编译成字节码,它提供了一套API让用户与java交互。因此clojure可以直接调用java世界中那些丰富庞大的优秀库了。
=>12.56 12.56在clojure中,我们使用数据时似乎并没有像其他语言那样需要一些特殊的处理或者声明。但是,clojure在底层实际创建了java对象,并指定了对应的java类型。我们可以通过class函数来看一下编译器为我们创建的数据的类型。
=>(class 12.56) java.lang.Double上面我们可以看出,clojure自动为我们创建了一个Double类型的java对象。
如果我们想在clojure指定对应的java类型,可以这么做:
=>(new java.lang.Float 12.56) 12.56 =>(class (new java.lang.Float 12.56)) java.lang.Floatnew 也是一个函数,我们使用它创建了一个Float类型的对象,貌似比java还麻烦。clojure给我们提供了一个更简洁的语法来做同样的事情。我们在对应的构造函数名字后加一个点,然后后面依次写上构造函数需要的参数即可。
=>(Float. "12.56") 12.56 =>(class (Float. "12.56")) java.lang.Float这里 Float. 就是Float的构造函数名加上一个点。这可不是clojure的函数调用。我们可以使用fn?测试一下,Floag.并不是函数。应该只是clojure的特殊的语法调用吧。
下面是一些将字符串转换为数字的例子:
=>(+ "12.56" "5.92") java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Number =>(class "12.56") java.lang.String =>(+ (Float. "12.56")(Float. "5.92")) 18.48 =>(+ (Float. "12.5648230948234")(Float. "5.92")) 18.484823 =>(+ (Double. "12.5648230948234")(Double. "5.92")) 18.484823094823398
clojure本身并没有专门的日期和时间处理函数。不过不用担心,java有的功能,clojure基本上都可以去调用。而且一旦我们用clojure函数将java的方法包装后,我们后面的调用就再也不会和java打交道了。
下面就看看如何使用java接口来创造我们自己的日期时间函数。让我们从调用系统当前时间开始吧。这一次我们先采用一种“斜线调用”形式。我们使用斜线一次隔开类名和调用的静态方法名称。我们将要调用java中的 System.currentTimeMillis()方法来获取当前系统时间的毫秒数。
=>(defn msec [] ;;使用函数msec包装 (System/currentTimeMillis)) ;;这里的斜线相当于java中的 "." #'user/msec =>(msec) ;;调用msec,返回当前系统时间毫秒数 1307328017335 =>(msec) 1307662973116 =>(> 1307328017335 1307662973116) false =>(> 1307662973116 1307328017335) true
上面是静态方法调用的展示,下面再来看看如果调用实例方法。让我们创建两个java.util.Date对象。注意,之前我们使用的都是java.lang下面的类,所以不需要手动导入。非lang包下的必须手动导入(和java一样一样的)。
=>(Date.) ;;导入前,找不到Date类 java.lang.IllegalArgumentException: Unable to resolve classname: Date... =>(import java.util.Date) ;;手动导入Date (语法都和java一样) java.util.Date =>(Date.) ;;再次创建Date对象 #<Date Sun Jun 05 20:48:19 MDT 2011> =>(class (Date.)) ;;查看一下对象类型,确实是 java.util.Date java.util.Date
现在我们来用clojure来包装一下,我们创造一个名为date的函数。该函数有两种参数形式:无参数和接受毫秒值(对应Date 的无参构造函数和 Date(long time)构造函数)。
=>(defn date ;;使用函数date封装java API ([](Date.)) ;;第一种是无参数模式 ([systime](Date. systime))) ;;第二种是接受一个参数 #'user/date =>(date) ;;调用date函数,无参数 #<Date Sun Jun 05 20:41:42 MDT 2011> =>(date 1307328017335) ;;调用date函数,传入一个long值 #<Date Sun Jun 05 20:40:17 MDT 2011>为了让我们时间处理在强大点,我们在利用java库中的 SimpleDateFormat类将时间转换成特定形式的字符串。
=>(import java.text.SimpleDateFormat) ;;导入需要的类 java.text.SimpleDateFormat =>(defn format-date ;;使用自定义函数封装 ([](format-date (date) "yyyy MM dd HH mm ss")) ([x](if (string? x) (format-date (date) x) (format-date x "yyyy MM dd HH mm ss"))) ([dt fmt](.format (SimpleDateFormat. fmt) dt))) #'user/format-date =>(format-date) "2011 06 04 17 50 21" =>(format-date (date 404534000000)) "1982 10 26 20 33 20" =>(format-date "yyyy/MM/dd HH:mm:ss") "2011/06/04 17:51:00" =>(format-date (date 404534000000) "yyyy/MM/dd HH:mm:ss") "1982/10/26 20:33:20"其他没有什么难点,都是之前说过的内容,我们主要解释上面代码中下面这一句的意义:
([dt fmt](.format (SimpleDateFormat. fmt) dt)))[dt fmt]是参数列表,这个没什么特别的东西。主要看看函数体。 .format 是方法名 (SimpleDateFormat. fmt)最终返回的是 SimpleDateFormat类型的对象(相当于调用 new SimpleDateFormat(fmt)) ,dt 是参数所以整个意思是调用实例 (SimpleDateFormat. fmt)的format 方法,并传入参数dt。翻译成java代码就是:
(new SimpleDateFormat(fmt)).format(dt)很简单吧。个人觉得使用clojure封装后的java代码比原本的java API更要紧凑和灵活。这主要依靠了clojure这种动态语言+函数式语言的N多优势。