在运行时访问clojure类型提示

时间:2012-07-04 10:17:14

标签: types clojure

假设我定义了包含一些类型提示的记录,如下所示:

(defrecord person [name sex ^Integer age city])

有没有办法在运行时使用person类或person的实例来确定指定了哪些类型的提示?目的是根据字段的类型更改使用的gui组件(请注意,字段的值可能为nil,因此我无法使用值的类型来确定字段的类型)。 / p>

我尝试了一些显而易见的事情,但没有到达任何地方:

; no metadata on the class, an instance, or the keys or vals of an instance
=> (meta person)
nil
=> (meta (person. "Geoff" "male" 30 "Moon base"))
nil
=> (map meta (keys (person. "Geoff" "male" 30 "Moon base")))
(nil nil nil nil)
=> (map meta (vals (person. "Geoff" "male" 30 "Moon base")))
(nil nil nil nil)
; the field is of type Object
=> (filter (fn [x] (= "age" (.getName x))) (.getFields person))
(#<Field public final java.lang.Object matt.clarity.scratch.person.age>)
; no metadata on the fields of the class
=> (map meta (.getFields person))
(nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil)

3 个答案:

答案 0 :(得分:3)

我认为你应该从不同的角度来看待这个问题:

  • 创建定义字段和数据类型的地图
  • 使用此地图生成相应的defrecord和任何查看者/属性编辑器

地图可能如下所示:

  {:name java.lang.String
   :sex  java.lang.String
   :age  java.lang.Integer}

然后,此地图将作为您的元数据,驱动系统的其余部分......

答案 1 :(得分:2)

似乎在记录字段上应用类型提示对反射没有任何影响,因此会被忽略。

user=> (set! *warn-on-reflection* true)
user=> (defrecord user [^String name])     
user=> (.indexOf "z" (:name (user. "Superman")))
Reflection warning, NO_SOURCE_PATH:16 - call to indexOf can't be resolved.
-1

另外,我不会建议使用类型提示来处理与业务逻辑相关的内容,我更愿意使用自己的数据标记来描述要在UI控件中显示的数据类型。可以定义一个协议,该协议具有获取特定类型的字段类型的方法,然后在定义记录时在您的记录上实现该协议。

类似的东西:

user=> (defprotocol DescribeData (getFieldTypes [this]))
user=> (defrecord User [name age] DescribeData (getFieldTypes [_] {:name String :age Integer}))
user=> (def u (User. "Superman" 1000))
user=> (getFieldTypes u)
{:name java.lang.String, :age java.lang.Integer}

通过这种方式,您可以将任何对象传递到UI层,UI层可以与任何实现协议的对象一起使用。

答案 2 :(得分:1)

在运行时没有简单的方法来访问类型提示。你必须在你自己的宏中包装defrecord,它可以访问这样的提示:

(->> (-> '(defrecord Foo [^Integer a b c]) 
         macroexpand-1
         (nth 2)
         (nth 3))
     (drop-last 2)
     (map #(vec [% (-> % meta :tag)]))) ; => ([a Integer] [b nil] [c nil])

但是,我宁愿建议使用@ mikera的答案并构建一个发出defrecord和defvar的宏来保存field-gt;类型的映射。