【问题标题】:Extending Clojure's Associative Abstraction to Java Library Types将 Clojure 的关联抽象扩展到 Java 库类型
【发布时间】:2017-08-30 00:32:59
【问题描述】:

我有一个应用程序(实际上是几个),它使用 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 不是协议(如果是这样的话,它会起作用)。第二个是类型已经定义,所以我不能用 deftype 添加 Associative。

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

谢谢!

【问题讨论】:

    标签: clojure clojure-java-interop


    【解决方案1】:

    答案是你想做的扩展已经做了一半,另一半做不到。 get-in 函数调用get,它调用clojure.lang.RT/get,它调用clojure.lang.RT/getFrom,如果第一个参数是Map,它调用java.util.Map/get。因此,如果您有任何 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 没有支持 RandomAccessLists 的 get 实现。你可以写你自己的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 函数。

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

    【讨论】:

    • 谢谢 Sam,听说这个问题没有真正好的解决方案很有用。我很幸运,实际上这一切都归结为少数几个呼叫站点,并且目前采取了类似的方法。我可能会进一步缩小它并利用 RT/get on your 建议。谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-06-28
    • 1970-01-01
    • 2019-01-22
    • 2013-05-20
    • 2017-11-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多