Clojure: невозможно найти статическое поле

Учитывая следующий фрагмент кода:

(map Integer/parseInt ["1" "2" "3" "4"])

Почему я получаю следующее исключение, если я не обернул Integer/parseInt в анонимную функцию и не вызвал ее вручную (#(Integer/parseInt %))?

clojure.lang.Compiler$CompilerException: java.lang.RuntimeException: Unable to find static field: parseInt in class java.lang.Integer

Ответ 1

документация по java interop говорит следующее:

Предпочтительные идиоматические формы для доступа к членам поля или метода приведены выше. Форма члена экземпляра работает как для полей, так и для методы. Форма instanceField предпочтительна для полей и требуется если существует и поле, и метод с 0 аргументами с тем же именем. Oни все разворачиваются в вызовы оператору точки (описанные ниже) в время макрорасширения. Расширения заключаются в следующем:... (Classname/STATICMETHOD args *) == > (. Classname staticMethod args *) Имя класса /staticField == > (. Classname staticField)

поэтому вы должны помнить, что Class/fieldName - это просто сахар для получения статического поля , ни статический метод, ни ссылка на статический метод (java-метод действительно не является функцией clojure), поэтому в Integer class нет статического поля parseInt, а (Class/fieldName arg) вызывает статический метод, они два совершенно разные операции, используя аналогичный сахаритный синтаксис.

поэтому, когда вы делаете (map #(Integer/parseInt %) ["1" "2" "3" "4"]), он расширяется до

(map #(. Integer parseInt %) ["1" "2" "3" "4"])

(вы можете легко увидеть его самостоятельно с помощью макрорасширения),

и (map Integer/parseInt ["1" "2" "3"]) расширяется до

(map (. Integer parseInt) ["1" "2" "3"])

Он терпит неудачу, когда он пытается получить поле (которое, по вашему мнению, получает ссылку на метод).

Ответ 2

Integer/parseInt является статическим методом класса Integer, а не функцией clojure. Каждая функция clojure скомпилирована в класс java, который реализует интерфейс clojure.lang.IFn. map ожидает функцию clojure (которая реализует интерфейс IFn) в качестве первого аргумента, однако Integer/parseInt нет.

Вы можете проверить это в clojure repl.

user=> (type map)
clojure.core$map
user=> (type Integer)
java.lang.Class
user=> (type Integer/parseInt)

CompilerException java.lang.RuntimeException: Unable to find static field: parseInt in class java.lang.Integer, compiling:(/private/var/folders/p_/psdvlp_12sdcxq07pp07p_ycs_v5qf/T/form-init4110003279275246905.clj:1:1)

user=> (defn f [] 1)
#'user/f
user=> (type f)
user$f
user=> (type #(1))
user$eval9947$fn__9948

Возможно, прочитав fooobar.com/questions/300303/..., вы поймете, что происходит.

Ответ 3

Возможно, вы захотите сделать это без взаимодействия с Java:

(map read-string ["1" "2"])

или для более безопасного варианта:

(map clojure.edn/read-string ["1" "2"])

Я лично предпочитаю как можно больше скомпенсировать использование Java в коде Clojure.

Что касается того, почему вы не можете просто передать функцию Java, потому что map ожидает функцию в Clojure, а не функцию в Java.