Types 在运行时访问clojure类型提示

Types 在运行时访问clojure类型提示,types,clojure,Types,Clojure,假设我定义了一个包含一些类型提示的记录,如下所示: (defrecord person [name sex ^Integer age city]) 是否有任何方法可以在运行时使用person类或person的实例来确定指定了哪些类型的提示?其目的是根据字段的类型更改所使用的gui组件(请注意,字段的值可能为零,因此我无法使用值的类型来确定字段的类型) 我尝试了一些显而易见的事情,但没有成功: ; no metadata on the class, an instance, or the key

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

(defrecord person [name sex ^Integer age city])
是否有任何方法可以在运行时使用
person
类或
person
的实例来确定指定了哪些类型的提示?其目的是根据字段的类型更改所使用的gui组件(请注意,字段的值可能为零,因此我无法使用值的类型来确定字段的类型)

我尝试了一些显而易见的事情,但没有成功:

; 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)
;类、实例或实例的键或VAL上没有元数据
=>(元人)
无
=>(meta(个人“杰夫”男性“30”月球基地)
无
=>(地图元(钥匙(人。“杰夫”“男性”30“月球基地”))
(零零零零零)
=>(地图元(VAL(个人“杰夫”男性“30”月球基地))
(零零零零零)
; 该字段的类型为Object
=>(过滤器(fn[x](=“age”(.getName x)))(.getFields-person))
(#)
; 类的字段上没有元数据
=>(映射元(.getFields person))
(零零零零零零零零零零零零零零零零零零零零零零零零零零零零零零零零零零零零零零零零零零零零零零零零零零零零零零零零零零)

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

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}
  {:name java.lang.String
   :sex  java.lang.String
   :age  java.lang.Integer}

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

我认为您应该从不同的角度来处理此问题:

  • 创建定义字段和数据类型的映射
  • 使用此映射可以生成适当的defrecord和任何查看器/属性编辑器
地图可能看起来像:

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}
  {:name java.lang.String
   :sex  java.lang.String
   :age  java.lang.Integer}

然后,此映射将用作元数据,驱动系统的其余部分….

在运行时没有简单的方法访问类型提示。您必须在自己的宏中包装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。

认为我可能必须这样做。正如@Ankur所建议的,在任何情况下,这都可能是一种更好的方法。我仍然有点惊讶,类型提示在运行时是不可访问的。@OpenSause,请注意元数据应用于变量,而不是它指向的对象。。因此,为了获得Var的元数据,我们需要使用
(meta#'myvar)