clojure - 将 Clojure 的关联抽象扩展到 Java 库类型

标签 clojure clojure-java-interop

我有一个应用程序(实际上有几个),它使用 Jackson 解码 Map 中的 JSON 数据。数据似乎位于 Map 或 ArrayList 中(对于 JSON 数组)。这些流中传入的数据是非结构化的,因此不会改变。

我拥有一些 Clojure 代码,可以访问这些对象中的嵌套属性。理想情况下,我想将关联抽象扩展到这些 Java 类型,以便 get-in 对它们起作用。类似于以下内容:

(extend-protocol clojure.lang.Associative
  java.util.Map
    (containsKey [this k] (.containsKey this k))
    (entryAt [this k] (when (.containsKey this k)
                  (clojure.lang.MapEntry/create k (.get this k))))
java.util.ArrayList
  (containsKey [this k] (< (.size this) k))
  (entryAt [this k] (when (.containsKey this k)
                  (clojure.lang.MapEntry/create k (.get this k)))))

这有两个问题;首先,Associative 不是一个协议(protocol)(如果是的话,它似乎可以工作)。第二个是类型已经定义,所以我无法添加与 deftype 关联。

我对 Clojure 的 JVM 互操作部分还很陌生。有没有我没有看到的方法?或者是否有一个协议(protocol)可以包装关联并且可以与我错过的 get-in 一起使用?

非常感谢!

最佳答案

答案是你想做的扩展一半已经完成,另一半无法完成。 get-in函数调用get ,它调用 clojure.lang.RT/get ,它调用 clojure.lang.RT/getFrom ,它调用 java.util.Map/get如果第一个参数是 Map。因此,如果您有任何 Java Map,那么 get-in 就可以工作(我直接从 doto 文档字符串借用这个示例):

(let [m (doto (new java.util.HashMap) (.put "a" 1) (.put "b" 2))]
  [(get-in m ["b"])
   (get-in m ["a"])])
;;=> [2 1]

但是,Clojure 没有 Listget 实现支持RandomAccess 。您可以编写自己的get来实现:

(ns sandbox.core
  (:refer-clojure :exclude [get])
  (:import (clojure.lang RT)
           (java.util ArrayList List RandomAccess)))

(defn get
  ([m k]
   (get m k nil))
  ([m k not-found]
   (if (and (every? #(instance? % m) [List RandomAccess]) (integer? k))
     (let [^List m m
           k (int k)]
       (if (and (<= 0 k) (< k (.size m)))
         (.get m k)
         not-found))
     (RT/get map key not-found))))

示例:

(get (ArrayList. [:foo :bar :baz]) 2)
;;=> :bar

然后你可以复制 get-in 的实现因此它将使用您的自定义 get 函数。

不过,我很确定这不是您想要的,因为这样您编写的每段代码都必须使用您的 get-in 而不是Clojure 的 get-in 以及任何其他已经使用 Clojure 的 get 的代码仍然无法与 ArrayList 一起使用。不幸的是,我认为对于您的问题没有真正好的解决方案。

关于clojure - 将 Clojure 的关联抽象扩展到 Java 库类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45950179/

相关文章:

clojure - 如何获取具有统一命名空间键的映射的命名空间?

clojure - 如何标记 Clojure 函数以便可以通过 Java 反射识别它

Clojure deftype 带有类型提示? : Can't find matching method, 留下自动匹配提示

mysql - 如何从 Clojure 连接到 MySQL 数据库?

java - PHP 和 Clojure (Java) 代码之间的原始 MD5 base64 编码字符串对于某些字符的不同结果

clojure - 如何从 Clojure 调用 Java 类的 eval 方法?

java - 在 Java 接口(interface)上调用 .class 时将 .class 传递给 Clojure 函数

java - 从 Java 应用程序内将参数传递到 Duckling Clojure 函数

clojure - 在生产中部署 Clojure/Clojurescript 应用程序

emacs - clojure.tools.namespace refresh-all 后 clojure.repl 命名空间丢失