Given the following piece of code:
(map Integer/parseInt ["1" "2" "3" "4"])
Why do I get the following exception unless I wrap Integer/parseInt
in an anonymous function and call it manually (#(Integer/parseInt %)
)?
clojure.lang.Compiler$CompilerException: java.lang.RuntimeException: Unable to find static field: parseInt in class java.lang.Integer
the documentation on java interop says the following:
The preferred idiomatic forms for accessing field or method members are given above. The instance member form works for both fields and methods. The instanceField form is preferred for fields and required if both a field and a 0-argument method of the same name exist. They all expand into calls to the dot operator (described below) at macroexpansion time. The expansions are as follows: ... (Classname/staticMethod args*) ==> (. Classname staticMethod args*) Classname/staticField ==> (. Classname staticField)
so you should remember, that Class/fieldName
is just a sugar for getting a static field, neither static method call, nor reference to the static method (java method is not a clojure function really), so there is no static field parseInt
in Integer class
, while (Class/fieldName arg)
calls a static method, they are two totally different operations, using the similar sugary syntax.
so when you do (map #(Integer/parseInt %) ["1" "2" "3" "4"])
it expands to
(map #(. Integer parseInt %) ["1" "2" "3" "4"])
(you can easily see it yourself with macroexpansion),
and (map Integer/parseInt ["1" "2" "3"])
expands to
(map (. Integer parseInt) ["1" "2" "3"])
It fails when it is trying to get a field (which you think is getting a reference to a method).
Integer/parseInt
is a static method of Integer
class, not a clojure function. Each clojure function is compiled to java class which implements clojure.lang.IFn
interface. map
expects clojure function (which implements IFn
interface) as a first argument, however, Integer/parseInt
is not.
You can check that in the 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
Perhaps reading this stackoverflow question will help you understand what is going on.
You might prefer to do it without the Java interop:
(map read-string ["1" "2"])
or for a more safe variant:
(map clojure.edn/read-string ["1" "2"])
I personally prefer to minimize the use of Java as much as possible in Clojure code.
As for why you can't just pass the Java function, because map
expects a function in Clojure, not a function in Java.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With