Вызов списка методов объекта Java с помощью Clojure

У меня есть список значений, которые я хочу преобразовать в неизменяемый Java POJO. Java POJO создается с использованием шаблона построителя.

Список у меня такой:

[49.0 11.1 150.0]

В Java мой POJO будет построен следующим образом:

Position.builder().latitude(49.0).longitude(11.1).altitude(150.0).build()

Я думал, что для создания его в Clojure я бы сначала zipmap значения с методами, которые нужно использовать, а затем я мог бы reduce-kv все к нужному мне значению Java:

(defn poslist->map [list]
      (zipmap ['.latitude '.longitude '.altitude]
              list))

(.build (reduce-kv #(%2 %1 %3) 
                   (pkg.Position/builder)
                   (poslist->map list)))

Моя проблема сейчас в том, что я не получаю того, что ожидаю. Функция poslist->map возвращает ожидаемое значение:

{.latitude 49.0, .longitude 11.1, .altitude 150.0}

Но reduce-kv просто возвращает последнее значение, которое у меня есть:

150.0

Почему я не получаю обратно билдер, к которому потом могу применить метод .build? Я ожидал, что reduce-kv будет иметь тот же результат, что и следующий код, который возвращает то, что я ожидаю:

(.build (.altitude (.longitude (.latitude (pkg.Position/builder) 150.0) 11.1) 49.0))

И поскольку функция reduce-kv возвращает двойное значение, следующий вызов .build терпит неудачу с

No matching field found: build for class java.lang.Double

Кстати: причина, по которой я сопоставляю методы со значениями списка, заключается в том, что фактический список полей, которые я должен установить, длиннее, чем в примере. Я сократил пример, чтобы сделать его более читабельным, сохранив при этом основную проблему.


person Matthias Wimmer    schedule 11.07.2016    source источник


Ответы (1)


В то время как символы Clojure действуют как функции< /a>, они никак не связаны с одноименными функциями:

> ('.add (java.util.ArrayList.) 1)
1
> ('first [1 2 3])
nil
> ('conj [1 2] 3)
3

Итак, проблема, по сути, в том, что методы Java вообще не вызываются. Чтобы исправить это, оберните методы iterop с помощью memfn:

['.latitude '.longitude '.altitude]

->

[(memfn latitude val) (memfn longitude val) (memfn altitude val)]

или, как предложил @noisesmith, с функциями clojure:

->

[#(.latitude %1 %2) #(.longitude %1 %2) #(.altitude %1 %2)]
person OlegTheCat    schedule 11.07.2016
comment
в настоящее время нет ситуации, когда (memfn foo) предпочтительнее #(.foo %) - person noisesmith; 12.07.2016
comment
Спасибо вам обоим, это работает! Только одно замечание: чтобы точно соответствовать моему вопросу, созданная функция принимает два параметра (экземпляр построителя и устанавливаемое значение). Следовательно, это либо (memfn latitude val), либо #(.latitude %1 %2). - person Matthias Wimmer; 12.07.2016