【问题标题】:How do I implement a Java interface in Clojure如何在 Clojure 中实现 Java 接口
【发布时间】:2011-12-23 10:09:50
【问题描述】:

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

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

Doer clojureDoer = ?;

String output = clojureDoer.doSomethin(input);

【问题讨论】:

  • 感谢大家帮助我!我最终使用了 Reify,只是在 clojure 中编写了 main 函数。 clojure 非常酷!

标签: java clojure


【解决方案1】:

reify 是实现接口的强烈首选 - proxy 是重型、旧且缓慢的,因此应尽可能避免使用。实现如下所示:

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

【讨论】:

    【解决方案2】:

    截至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");
        }
    }
    

    【讨论】:

    • 谢谢@Gary Verhaegen ;)
    【解决方案3】:

    带代理

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

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

    proxy 返回一个实现Doer 的对象。现在,要在 Java 中访问它,您必须使用 gen-class 使您的 Clojure 代码可以从 Java 中调用。 "Calling clojure from java" 问题的答案中对此进行了介绍。

    使用 gen-class

    (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

    【讨论】:

    • 注意“Doer clojureDoer = ?”在我的问题中。我在“?”中输入了什么?使程序工作。您提供的链接显示了如何将 clojure 对象作为静态类导入。谢谢!
    • 在这种情况下,您可能对我刚刚添加的答案的后半部分更感兴趣。它是proxy-free,似乎更适合您的情况。如果第二个解决了你的问题,我想我会删除前半部分。
    • 有趣.. 我不得不将 Doer 放入一个包中,因为编译器正在寻找 java.lang.Doer 否则。当我执行 Doer doer = new DoerClj(); 时,线程“main” java.lang.ClassFormatError: Duplicate method name&signature in class file DoerClj
    • 澄清一下 - 您使用的是哪个 Clojure 版本?
    • 奇怪。尝试基于Runnable 接口编译这个简单的example。当我(require 'doer-clj) (compile 'doer-clj) 时,我可以毫无问题地拨打DoerClj.run()
    【解决方案4】:

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

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

    【讨论】:

    • 我以前遇到过一次,但我没有意识到它真的是一个多么好的资源。感谢您指出这一点。
    【解决方案5】:

    如果在您的界面中定义了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.
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-05-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-03-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多