元数据 #

一、元数据简介 #

元数据是附加在数据上的额外信息,不影响数据本身的相等性判断。元数据常用于类型提示、文档、私有标记等。

1.1 特点 #

  • 不影响数据相等性
  • 不可变
  • 可以附加到大多数数据类型
  • 用于编译器优化和文档

1.2 查看元数据 #

clojure
(meta ^:private {:a 1})

(meta ^{:doc "A variable"} #'map)

(meta (with-meta [1 2 3] {:type :vector}))

二、添加元数据 #

2.1 元数据字面量 #

clojure
^{:private true} {:a 1}

^:private {:a 1}

^[String] {:name "test"}

^{:tag String} "hello"

2.2 with-meta #

clojure
(def data (with-meta [1 2 3] {:type :vector :count 3}))

(meta data)

data

2.3 vary-meta #

clojure
(def data [1 2 3])

(def data-with-meta
  (vary-meta data assoc :type :vector))

(meta data-with-meta)

2.4 在def中使用 #

clojure
(def ^:private secret "hidden")

(def ^{:doc "A constant" :const true} PI 3.14159)

(meta #'secret)

(meta #'PI)

三、元数据读取 #

3.1 meta #

clojure
(def data (with-meta {:a 1} {:source :test}))

(meta data)

(meta ^:private [1 2 3])

3.2 Var元数据 #

clojure
(defn my-function
  "This is a doc string"
  [x]
  (* x x))

(meta #'my-function)

(:doc (meta #'my-function))

(:arglists (meta #'my-function))

3.3 命名空间元数据 #

clojure
(ns my-ns
  {:author "Alice"
   :version "1.0.0"})

(meta (find-ns 'my-ns))

四、常用元数据 #

4.1 :private #

clojure
(def ^:private internal-value 42)

(defn ^:private helper []
  "internal helper")

4.2 :doc #

clojure
(def ^{:doc "The value of PI"} PI 3.14159)

(defn ^{:doc "Calculate square"} square [x] (* x x))

4.3 :tag (类型提示) #

clojure
(defn ^String process [^String s]
  (.toUpperCase s))

(defn add [^Long a ^Long b]
  (+ a b))

4.4 :const #

clojure
(def ^:const MAX-VALUE 100)

(def ^:const DEFAULT-TIMEOUT 5000)

4.5 :dynamic #

clojure
(def ^:dynamic *config* {:debug false})

(binding [*config* {:debug true}]
  *config*)

4.6 :deprecated #

clojure
(defn ^:deprecated old-function []
  "Use new-function instead")

五、元数据与集合 #

5.1 集合元数据 #

clojure
(def data (with-meta [1 2 3] {:type :vector}))

(meta data)

(meta (conj data 4))

(meta (seq data))

5.2 元数据保留 #

clojure
(def data (with-meta {:a 1} {:source :test}))

(meta (assoc data :b 2))

(meta (dissoc data :a))

(meta (into {} data))

5.3 合并元数据 #

clojure
(defn merge-with-meta [m1 m2]
  (let [merged (merge m1 m2)
        meta1 (meta m1)
        meta2 (meta m2)]
    (with-meta merged (merge meta1 meta2))))

(def m1 (with-meta {:a 1} {:source :test1}))
(def m2 (with-meta {:b 2} {:source :test2}))

(meta (merge-with-meta m1 m2))

六、元数据与函数 #

6.1 函数元数据 #

clojure
(defn my-function
  "Documentation string"
  {:added "1.0"
   :deprecated "2.0"}
  [x]
  (* x x))

(meta #'my-function)

6.2 参数元数据 #

clojure
(defn process [^String name ^Long age]
  (str name " is " age " years old"))

(meta #'process)

6.3 匿名函数元数据 #

clojure
(def f (with-meta
         (fn [x] (* x x))
         {:doc "Square function"}))

(meta f)

七、实践示例 #

7.1 验证元数据 #

clojure
(defn with-validation [data validator]
  (vary-meta data assoc :validator validator))

(defn validate [data]
  (if-let [validator (:validator (meta data))]
    (validator data)
    true))

(def valid-data
  (with-validation
    {:name "Alice" :age 30}
    #(and (string? (:name %))
          (pos? (:age %)))))

(validate valid-data)

7.2 来源追踪 #

clojure
(defn with-source [data source]
  (vary-meta data assoc :source source :timestamp (System/currentTimeMillis)))

(defn get-source [data]
  (select-keys (meta data) [:source :timestamp]))

(def user-data (with-source {:id 1 :name "Alice"} :api))

(get-source user-data)

7.3 缓存元数据 #

clojure
(defn with-cache-info [data ttl]
  (vary-meta data assoc
             :cached true
             :ttl ttl
             :created (System/currentTimeMillis)))

(defn is-cache-valid? [data]
  (let [{:keys [cached ttl created]} (meta data)]
    (and cached
         (< (- (System/currentTimeMillis) created) ttl))))

(def cached-data (with-cache-info {:result "data"} 60000))

(is-cache-valid? cached-data)

7.4 API版本控制 #

clojure
(defn with-api-version [data version]
  (vary-meta data assoc :api-version version))

(defn requires-version [data min-version]
  (let [version (:api-version (meta data))]
    (when (and version (< version min-version))
      (throw (ex-info "API version too old"
                      {:required min-version
                       :actual version})))))

(def v1-data (with-api-version {:users []} 1))

(requires-version v1-data 1)

八、元数据限制 #

8.1 不支持的类型 #

clojure
(meta 42)

(meta "string")

(meta :keyword)

数字、字符串、关键字等基本类型不支持元数据。

8.2 相等性 #

clojure
(= (with-meta {:a 1} {:x 1})
   (with-meta {:a 1} {:x 2}))

元数据不影响相等性判断。

九、元数据工具 #

9.1 查看所有元数据 #

clojure
(defn show-meta [obj]
  (let [m (meta obj)]
    (when m
      (doseq [[k v] m]
        (println k "=" v)))))

(show-meta #'map)

9.2 复制元数据 #

clojure
(defn copy-meta [from to]
  (with-meta to (meta from)))

(def source (with-meta {:a 1} {:source :test}))
(def target {:b 2})

(meta (copy-meta source target))

十、总结 #

元数据操作速查:

操作 函数
添加 with-meta, ^{:key val}
读取 meta
修改 vary-meta
合并 vary-meta ... merge

常用元数据键:

用途
:private 私有标记
:doc 文档字符串
:tag 类型提示
:const 常量标记
:dynamic 动态变量
:deprecated 废弃标记

关键点:

  1. 元数据不影响数据相等性
  2. 用于类型提示、文档、标记
  3. 大多数集合类型支持元数据
  4. with-meta 添加,meta 读取
  5. vary-meta 基于现有元数据修改

下一步,让我们学习状态与标识!

最后更新:2026-03-27