Как передать типизированную коллекцию из clojure в java?

Я знаю основы clojure/java interop: вызов java из clojure и наоборот. Однако я не смог вернуть типизированную коллекцию из clojure в java. Я пытаюсь увидеть что-то из этой природы List<TypedObject> из java-кода, который вызывает в clojure.

Java Object:

public class TypedObject {
    private OtherType1 _prop1;
    public OtherType1 getProp1() {
        return _prop1;
    }
    public void setProp1(OtherType1 prop1) {
        _prop1 = prop1;
    }
}

CLojure method:

(defn -createListOfTypedObjects
      "Creates and returns a list of TypedObjects"
      [input]
      ;Do work here  to create and return list of TypedObjects
      [typedObj1, typedObj2, typedObj3])

(:gen-class
 :name some.namespace
 :methods [createListofTypedObjects[String] ????])

Давайте рассмотрим, что я пишу API, используя clojure, который должен быть распространен как файл jar, который будет использоваться из java. Мой вопрос был действительно как к тому, что передать вместо???? вопросительные знаки выше внутри: gen-класса для AOT, так что программист, пишущий кусок кода в java, используя мой api, может иметь соответствующее завершение intellisense/code (т.е.: createListofTypedObjects() returns List<TypedObject>), например, из eclipse.

Ответы

Ответ 1

Другие правы, что Clojure не обеспечивает типы элементов в возвращенных коллекциях и т.д. (На самом деле, JVM не обеспечивает типы элементов в коллекциях, которые полностью обрабатываются javac.)

Однако я вижу значение предоставления API другим программистам на Java, который указывает интерфейс, который объявляет, что возвращаемые значения (или параметры) параметризуются различными способами; это особенно привлекательно, если вы хотите использовать Clojure в существующей среде Java, не создавая волн.

В настоящее время требуется двухэтапный процесс:

  • определить отдельный интерфейс (в Java!), который задает параметризованные типы, как вам нравится
  • определите пространство имен gen-class (или proxy или reify) таким образом, чтобы он реализовал этот интерфейс

(Clojure предоставляет форму definterface, которая позволит вам избежать определения отдельного интерфейса Java, но definterface, как и остальные Clojure, не предусматривает указания параметризованных типов. Возможно, когда-нибудь...: -))

например.

public interface IFoo {
    List<TypedObject> createListOfTypedObjects ();
}

а затем ваше пространство имен gen-class:

(ns your.ns.FooImpl
  (:gen-class
    :implements [IFoo]))
(defn -createListOfTypedObjects
  []
  [typedObj1, typedObj2, typedObj3])

Когда ваши пользователи создают экземпляры FooImpl, они будут, например, получите завершение кода, указав, что метод возвращает List<TypedObject>, а не Object или непараметрированный тип List.

Если вы используете инструменты правильной сборки (например, maven, gradle или правильно настроенный ant), тогда вы можете поместить интерфейс Java в свой проект Clojure, и будет зависеть кросс-язычная зависимость.

Ответ 2

Если вы пытаетесь передать что-то вроде List<String> в java-метод, вам не нужно беспокоиться об этом. Параметр типа (например, String) используется только компилятором javac, поэтому любой List будет работать нормально во время выполнения.

С другой стороны, если вы пытаетесь передать массив определенного типа объекта (например, String[]), вы можете использовать различные функции -array:

user=> (make-array String 10)            ; an empty String array
#<String[] [Ljava.lang.String;@78878c4c>
user=> (into-array ["foo" "bar"])        ; array type inferred from first element
#<String[] [Ljava.lang.String;@743fbbfc>
user=> (into-array Number [1.2 5 7N])    ; explicit type array
#<Number[] [Ljava.lang.Number;@7433b121>

Ответ 3

Вам не нужно беспокоиться о дженериках (типизированных коллекциях) в Clojure. Генерики - это просто подсказки типа для компилятора Java. В текущей программе Java List<String> фактически совпадает с List<Object>.

Итак, например, вектор Clojure, содержащий строки, уже является List<String> без необходимости преобразования.