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.