如何在 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")