2016-02-04 16 views
10

Dato il seguente pezzo di codice:Clojure: Impossibile trovare campo statico

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

Perché ottengo la seguente eccezione a meno che non mi avvolgo Integer/parseInt in una funzione anonima e chiamare manualmente (#(Integer/parseInt %))?

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

in aggiunta alle eccellenti risposte che hai già - Clojure è un'astrazione abbastanza sottile sulla jvm, e non esiste un metodo di prima classe, quindi un metodo non può essere un argomento per una funzione Clojure. Cioè, a livello di codice byte, un metodo non può essere un argomento. – noisesmith

risposta

14

la documentazione java interoperabilità dice il seguente:

Le forme idiomatiche preferiti per accedere membri campo o il metodo sono già stati descritti sopra. Il modulo membro di istanza funziona sia per i campi che per i metodi . Il modulo instanceField è preferito per i campi e richiesto se esiste un campo e un metodo di argomento 0 con lo stesso nome. Essi si espandono tutte in chiamate all'operatore punto (descritto di seguito) al tempo di espansione macro . Le espansioni sono i seguenti: (. Classname staticField) ... (Classname/staticmethod args *) ==> (args. Classname staticmethod *) Classname/staticField ==>

così si dovrebbe ricordate che Class/fieldName è solo uno zucchero per ottenere una statica campo, né statico chiamata al metodo, nè riferimento al metodo statico (metodo Java non è una funzione clojure davvero), quindi non c'è campo statico parseInt in Integer class, mentre (Class/fieldName arg) chiama uno stato c metodo, sono due operazioni totalmente diverse, utilizzando la sintassi zuccherina simile.

in modo che quando si fa (map #(Integer/parseInt %) ["1" "2" "3" "4"]) si espande per

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

(si può facilmente vedere da soli con macroexpansion),

e (map Integer/parseInt ["1" "2" "3"]) espande per

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

E ' fallisce quando sta cercando di ottenere un campo (che tu t hink sta ricevendo un riferimento a un metodo).

1

Si potrebbe preferire di farlo senza l'interoperabilità Java:

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

o per una variante più sicura:

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

Io personalmente preferisco per minimizzare l'uso di Java il più possibile nel codice Clojure.

Per quanto riguarda il motivo per cui non è possibile passare semplicemente la funzione Java, perché map prevede una funzione in Clojure, non una funzione in Java.

+1

'read-string' è quasi sempre una cattiva idea. la documentazione dice: "Si noti che read-string può eseguire codice (controllato da * read-eval *), e come tale dovrebbe essere usato solo con fonti attendibili Per la struttura di dati interop si usa clojure.edn/read-string". prova questo nel tuo repl '(read-string" # = (spit \ "./ filename.sh \" \ "evil code \") ")'. Poiché quasi sempre si utilizza la conversione dei dati per i dati esterni, 'read-string' è molto pericoloso qui – leetwinski

+0

@leetwinski grazie, ho aggiornato la mia risposta. – johnbakers

3

Integer/parseInt è un metodo statico della classe Integer, non una funzione di clojure. Ogni funzione di clojure è compilata alla classe java che implementa l'interfaccia clojure.lang.IFn. map prevede la funzione clojure (che implementa l'interfaccia IFn) come primo argomento, tuttavia non lo è Integer/parseInt.

È possibile controllare che nel 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 

Forse leggendo this stackoverflow question vi aiuterà a capire cosa sta succedendo.

+0

Pensando in termini di Clojure (piuttosto che di implementazione Java), si direbbe che l'argomento della funzione per la funzione 'map' è il primo argomento. Un po 'di confusione per sentirlo chiamò il secondo argomento. –

+0

@ChrisMurphy Hai ragione. È stato un errore. Ho aggiornato il post. – ntalbs

Problemi correlati