簡體   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