Clojure vars and Java static methods

前端 未结 3 2072
再見小時候
再見小時候 2020-12-06 03:05

I\'m a few days into learning Clojure and are having some teething problems, so I\'m asking for advice.

I\'m trying to store a Java class in a Clojure var and call i

3条回答
  •  北海茫月
    2020-12-06 03:32

    (Update: I've prepared something which might be acceptable as a solution... The original answer remains below a horizontal rule towards the end of the post.)


    I've just written a macro to enable this:

    (adapter-ns java.lang.reflect.Modifier jmod)
    ; => nil
    (jmod/isStatic 1)
    ; => false
    (jmod/isStatic 8)
    ; => true
    

    The idea is to create a single-purpose namespace, import the statics of a given class as Vars into that namespace, then alias the namespace to some useful name. Convoluted, but it works! :-)

    The code is looks like this:

    (defmacro import-all-statics
      "code stolen from clojure.contrib.import-static/import-static"
      [c]
      (let [the-class (. Class forName (str c))
            static? (fn [x]
                      (. java.lang.reflect.Modifier
                         (isStatic (. x (getModifiers)))))
            statics (fn [array]
                      (set (map (memfn getName)
                                (filter static? array))))
            all-fields (statics (. the-class (getFields)))
            all-methods (statics (. the-class (getMethods)))
            import-field (fn [name]
                           (list 'def (symbol name)
                                 (list '. c (symbol name))))
            import-method (fn [name]
                            (list 'defmacro (symbol name)
                                  '[& args]
                                  (list 'list ''. (list 'quote c)
                                        (list 'apply 'list
                                              (list 'quote (symbol name))
                                              'args))))]
        `(do ~@(map import-field all-fields)
             ~@(map import-method all-methods))))
    
    (defmacro adapter-ns [c n]
      (let [ias (symbol (-> (resolve 'import-all-statics) .ns .name name)
                        "import-all-statics")]
        `(let [ns-sym# (gensym (str "adapter_" ~n))]
           (create-ns 'ns-sym#)
           (with-ns 'ns-sym#
             (clojure.core/refer-clojure)
             (~ias ~c))
           (alias '~n 'ns-sym#))))
    

    The above looks up the Var holding the import-all-statics macro in a somewhat convoluted way (which is, however, guaranteed to work if the macro is visible from the current namespace). If you know which namespace it's going to be found in, the original version I've written is a simpler replacement:

    (defmacro adapter-ns [c n]
      `(let [ns-sym# (gensym (str "adapter_" ~n))]
         (create-ns 'ns-sym#)
         (with-ns 'ns-sym#
           (clojure.core/refer-clojure)
           ;; NB. the "user" namespace is mentioned below;
           ;; change as appropriate
           (user/import-all-statics ~c))
         (alias '~n 'ns-sym#)))
    

    (Original answer below.)

    I realise that this is not really what you're asking for, but perhaps clojure.contrib.import-static/import-static will be useful to you:

    (use 'clojure.contrib.import-static)
    
    (import-static clojure.lang.reflect.Modifier isPrivate)
    
    (isPrivate 1)
    ; => false
    (isPrivate 2)
    ; => true
    

    Note that import-static imports static methods as macros.

提交回复
热议问题