如何在Clojure中调用.java中的代码

最近我用Clojure写的Hive UDF中需要使用 IPIP 查询IP地理位置,有现成的 Java版解析器:https://github.com/ilsanbao/17moncn/tree/master/java ,为了完成工作进度,把它转成Clojure成本是比较高的,所以需要把它直接引入到Clojure中。

因为Clojure代码和Java代码都最终会转成JVM解析的字节码,所以它们可以互相之间调用,这个过程Leiningen可以帮我们自动完成。为了让Leiningen找到Java写的代码,首先在project.clj中设置Java源码存放的路径:

:java-source-paths ["src/java"]

这里Java源码存放在src/java目录中的,src/java中可以有子目录,Leiningen会自动遍历。以下是我的目录结构:

src
├── java
│  └── main
│       └── IpLocation.java
└── xx
    └── core.clj

IpLocation.java放在src/java/main目录中的,IpLocation.java中定义了一个IpLocation类,现在我们在Clojure的REPL中测试看是否能直接调用:

$ lein repl

Leiningen在加载之前会自动把.java编译.class,这个过程会被REPL打印出来:

Compiling 1 source files to /tmp/ip/xx/target/classes

如果没有报错,可以顺利进入REPL。然后把IpLocation这个类import进来,再新建一个IpLocation对象:

xx.core=> (import 'IpLocation)
IpLocation
xx.core=> (IpLocation.)
CompilerException java.lang.IllegalArgumentException: No matching ctor found for class IpLocation, compiling:(/tmp/form-init7230766958856873441.clj:1:1)

Oh,这里报错了:No matching ctor found for class IpLocation,需要把IpLocation类设置成pulibc才行。退出REPL,修改IpLocation.java的源码,把它设置成public:

public class IpLocation {
    ...
}

再运行lein repl进入交互式,并把IpLocation这个类import进来:

xx.core=> (import 'IpLocation)
IpLocation
xx.core=> (def ip-location (IpLocation.))
#'xx.core/ip-location
xx.core=> (.find ip-location "8.8.8.8")  ; find方法返回的是一个String数组
#<String[] [Ljava.lang.String;@2a476465>
xx.core=> (seq (.find ip-location "8.8.8.8")) ; 把find的返回结果转换成序列
("GOOGLE" "GOOGLE")