2016-02-04 3 views
10

Учитывая следующий фрагмент кода: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 
+2

в дополнение к отличным ответам, которые у вас уже есть - Clojure - довольно тонкая абстракция над jvm, и нет такой вещи, как метод первого класса, поэтому метод не может быть аргументом функции Clojure. То есть, на уровне байтового кода метод не может быть аргументом. – noisesmith

ответ

14

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

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

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

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

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

(вы можете легко увидеть это сами с macroexpansion),

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

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

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

1

Вы можете предпочесть сделать это без Java Interop:

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

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

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

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

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

+1

'read-string' почти всегда плохая идея. документация говорит: «Обратите внимание, что read-string может выполнять код (контролируемый * read-eval *), и как таковой должен использоваться только с доверенными источниками. Для структуры данных interop используйте clojure.edn/read-string". попробуйте это в вашей repl '(read-string" # = (spit \ "./ filename.sh \" \ "злой код \") ")'. Поскольку вы почти всегда используете преобразование данных для внешних данных, 'read-string' здесь очень небезопасен – leetwinski

+0

@leetwinski спасибо, я обновил свой ответ. – johnbakers

3

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

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

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 

Возможно чтение this stackoverflow question поможет вам понять, что происходит.

+0

Думая о Clojure (а не в реализации Java), вы бы сказали, что аргумент функции для функции «map» является первым аргументом. Немного запутанно слышать, что это называется вторым аргументом. –

+0

@ChrisMurphy Вы правы. Это была ошибка. Я обновил сообщение. – ntalbs

Смежные вопросы