2.6 元数据
在Clojure中,元数据(metadata) 是与对象逻辑值正交的数据。
如person的firstname 和 lastname是普通旧数据。而person对象可以被序列化到XML文件的信息就是元数据。同样person对象被更改需要保存到数据库的信息也是元数据。
可以使用with-meta给集合(collection)和符号(symbol)添加元数据:
(with-meta object metadata)
如:
(def stu {:name "Stu" :email "
[email protected]"})
(def serializable-stu (with-meta stu {:serializable true}))
说明:
(with-meta stu {:serializable true})给stu添加元数据,说明stu可序列化
metadata不会对改变原对象依赖对象值的操作结果,即添加元数据前和后的对象是相等的:
user=> (= stu serializable-stu)
true
=判断值是否相等,类似Java的equals。如果要判断是否同一个对象,需要使用identical?谓语:
(identical? obj1 obj2)
identical?类似Java的==,可以用identical?判断stu和serializable-stu是不同的对象:
user=> (identical? stu serializable-stu)
false
可以使用meta宏访问元数据,如:
user=> (meta stu)
nil
user=> (meta serializable-stu)
{:serializable true}
meta宏对应的读者宏为^:
user=> ^stu
nil
user=> ^serializable-stu
{:serializable true}
元数据可以被继承,在已有对象的基础上创建新对象,则新对象拥有已有对象的元数据。
user=> (def stu-with-address (assoc serializable-stu :state "NC"))
#'user/stu-with-address
user=> ^stu-with-address
{:serializable true}
说明:assoc函数给map添加key/value对象,然后返回新map
读者元数据
给变量var添加key/value对,使用元数据读者宏:
#^metadata form
创建shout函数,将字符串变大写,然后使用:tag关键字说明只能接受字符串参数并返回字符串值:
user=> (defn #^{:tag String} shout [#^{:tag String} s] (.toUpperCase s))
#'user/shout
如果试图传入非字符串参数,则抛出异常:
user=> (shout 1)
java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String (NO_SOURCE_FILE:0)
:tag元数据有一个简写形式:
#^Classname 等同 #^{:tag Classname}
shout函数可以重写为:
user=> (defn #^String shout [#^String s] (.toUpperCase s))
#'user/shout
为了便于阅读函数定义,还可以将元数据放到函数定义最后:
user=> (defn shout
([s] (.toUpperCase s))
{:tag String})
#'user/shout
常见元数据key如下表所示:
MetadataKey Used For
:arglists Parameter info used by doc
:doc Documentation used by doc
:file Source file
:line Source line number
:macro True for macros
:name Local name
:ns Namespace
:tag Expected argument or return type
说明:元数据读者宏与with-meta不同,元数据读者宏给编译器添加元数据,而with-meta给自定义的数据添加元数据。
user=> (def #^{:testdata true} foo (with-meta [1 2 3] {:order :ascending}))
#'user/foo
user=> (meta #'foo)
{:ns #, :name foo, :file "NO_SOURCE_PATH", :line 29, :testdata true}
user=> (meta foo)
{:order :ascending}
说明:一般来说,给变量和参数添加元数据使用元数据读者宏,给数据添加元数据使用with-meta。