代码之家  ›  专栏  ›  技术社区  ›  Cody

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

  •  2
  • Cody  · 技术社区  · 7 年前

    我拥有一些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)))))
    

    这有两个问题;第一个是,关联不是一个协议(如果是这样的话,它似乎会工作)。第二个是类型已经定义,所以我不能添加与deftype相关的。

    上车 我错过了什么?

    非常感谢!

    1 回复  |  直到 7 年前
        1
  •  2
  •   Community Egal    4 年前

    答案是,你想做的扩展有一半已经完成,另一半无法完成。这个 get-in 函数调用 get ,调用 clojure.lang.RT/get ,调用 clojure.lang.RT/getFrom ,调用 java.util.Map/get Map 地图 然后 上车 有效(我直接从 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没有 收到 实施 List 这就是支持 RandomAccess . 你 自己写 收到 这就是:

    (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
    

    然后您可以复制 上车 因此它将使用您的自定义 收到 作用

    我很确定这不是你想要的,因为你写的每一段代码都需要用到 上车 而不是Clojure的 ,以及任何其他已经使用Clojure的代码 收到 ArrayList