繁体   English   中英

如何在 Clojure 中实现 Java 接口

[英]How do I implement a Java interface in Clojure

如何创建一个实现此接口的 Clojure 对象,然后从 Java 代码中调用?

public interface Doer {
   public String doSomethin(String input);
}

Doer clojureDoer = ?;

String output = clojureDoer.doSomethin(input);

reify强烈推荐用于实现接口 - proxy是重型、老旧且缓慢的,因此应尽可能避免。 一个实现看起来像:

(reify Doer
  (doSomethin [this input]
    (...whatever...)))

Clojure 1.6 开始,首选方法如下。 假设您的类路径上有 Clojure 1.6 jar 和以下 clojure 文件(或其编译后的等效文件):

(ns my.clojure.namespace
  (:import [my.java.package Doer]))

(defn reify-doer
  "Some docstring about what this specific implementation of Doer
  does differently than the other ones. For example, this one does
  not actually do anything but print the given string to stdout."
  []
  (reify
    Doer
    (doSomethin [this in] (println in))))

然后,从 Java,您可以按如下方式访问它:

package my.other.java.package.or.maybe.the.same.one;

import my.java.package.Doer;
import clojure.lang.IFn;
import clojure.java.api.Clojure;

public class ClojureDoerUser {
    // First, we need to instruct the JVM to compile/load our
    // Clojure namespace. This should, obviously, only be done once.
    static {
        IFn require = Clojure.var("clojure.core", "require");
        require.invoke(Clojure.read("my.clojure.namespace"));
        // Clojure.var() does a somewhat expensive lookup; if we had more than
        // one Clojure namespace to load, so as a general rule its result should
        // always be saved into a variable.
        // The call to Clojure.read is necessary because require expects a Clojure
        // Symbol, for which there is no more direct official Clojure API.
    }

    // We can now lookup the function we want from our Clojure namespace.
    private static IFn doerFactory = Clojure.var("my.clojure.namespace", "reify-doer");

    // Optionally, we can wrap the doerFactory IFn into a Java wrapper,
    // to isolate the rest of the code from our Clojure dependency.
    // And from the need to typecast, as IFn.invoke() returns Object.
    public static Doer createDoer() {
        return (Doer) doerFactory.invoke();
    }
    public static void main(String[] args) {
        Doer doer = (Doer) doerFactory.invoke();
        doer.doSomethin("hello, world");
    }
}

带代理

请参阅proxy宏。 Clojure Docs有一些例子。 它也在Java Interop页面上进行了介绍。

(proxy [Doer] []
  (doSomethin [input]
    (str input " went through proxy")))

proxy返回一个实现Doer的对象。 现在,要在 Java 中访问它,您必须使用gen-class使您的 Clojure 代码可从 Java 调用。 它包含在“从 Java 调用 clojure”问题的答案中。

同级

(ns doer-clj
  (:gen-class
    :name DoerClj
    :implements [Doer]
    :methods [[doSomethin [String] String]]))

(defn -doSomethin
  [_ input]
  (str input " went through Clojure"))

现在将它保存为doer_clj.cljmkdir classes并通过调用你的 REPL (require 'doer-clj) (compile 'doer-clj) 您应该在classes目录中找到可以从 Java 中使用的DoerClj.class

对于这个问题的更一般性的看法,当您需要某种 Java 互操作时,此图可能非常有用:

https://github.com/cemerick/clojure-type-selection-flowchart

如果doSomethin()在你的接口定义,你应该在提到它:methods 引自http://clojuredocs.org/clojure_core/clojure.core/gen-class

:methods [ [name [param-types] return-type], ...]
The generated class automatically defines all of the non-private
methods of its superclasses/interfaces. This parameter can be used
to specify the signatures of additional methods of the generated
class. Static methods can be specified with ^{:static true} in the
signature's metadata. Do not repeat superclass/interface signatures
here.

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM