1    Introduction

1.1    Overview

std.lib.class is part of the standard foundation library set. This page collects the public API reference for the namespace.

2    Walkthrough

2.1    Class predicates

std.lib.class provides predicates and accessors for Java class metadata: arrays, primitives, interfaces, abstract classes, and ancestors.

inspect class flags

^{:refer std.lib.class/class:array? :added "3.0"}
(class:array? (type (int-array 0)))
=> true

^{:refer std.lib.class/primitive? :added "3.0"}
(primitive? Integer/TYPE)
=> true

^{:refer std.lib.class/class:interface? :added "3.0"}
(class:interface? java.util.Map)
=> true

^{:refer std.lib.class/class:abstract? :added "3.0"}
(class:abstract? java.util.AbstractMap)
=> true

2.2    Primitive conversions

primitive converts between the many representations of a Java primitive: keyword, symbol, raw JVM descriptor, class, and boxed container.

convert primitive representations

^{:refer std.lib.class/primitive :added "3.0"}
(primitive Boolean/TYPE :symbol)
=> 'boolean

^{:refer std.lib.class/primitive :added "3.0"}
(primitive "Z" :symbol)
=> 'boolean

^{:refer std.lib.class/primitive :added "3.0"}
(primitive 'long :container)
=> Long

2.3    Hierarchy traversal

ancestor:list, ancestor:tree, and class:interfaces walk the type hierarchy of a class.

walk class hierarchies

^{:refer std.lib.class/ancestor:list :added "3.0"}
(ancestor:list clojure.lang.PersistentHashMap)
=> [clojure.lang.PersistentHashMap
    clojure.lang.APersistentMap
    clojure.lang.AFn
    java.lang.Object]

^{:refer std.lib.class/class:interfaces :added "3.0"}
(class:interfaces clojure.lang.AFn)
=> #{java.lang.Runnable
     java.util.concurrent.Callable
     clojure.lang.IFn}

3    API



+primitive-lookup+ ^

NONE
(def +primitive-lookup+
  (create-lookup +primitive-records+ #{:size :bytes}))
link

+primitive-records+ ^

NONE
(def +primitive-records+
  (->> +primitives+
       (mapv (fn [[k [raw ^Class container type-fn array-fn no-size?]]]
               (let [kstr (name k)
                     cls (.get (.getField container "TYPE") nil)
                     base {:type k
                           :raw raw
                           :symbol (symbol kstr)
                           :clssym (symbol (.getName container) "TYPE")
                           :clstag (symbol (str cls))
                           :string kstr
                           :container container
                           :class cls}
                     size (if-not no-size?
                            {:size  (.get (.getField container "SIZE") nil)
                             :bytes (.get (.getField container "BYTES") nil)})
                     types (if (not= k :void)
                             {:type-fn type-fn
                              :array-raw (str "[" raw)
                              :array-fn array-fn
                              :array-class (Class/forName (str "[" raw))})]
                 [k (merge base size types)])))
       (into {})))
link

+primitives+ ^

NONE
(def +primitives+)
link

ancestor:all ^

[cls]
Added 3.0

returns all ancestors for a given type, itself included

v 3.0
(defn ancestor:all
  ([cls]
   (let [bases (ancestor:list cls)
         interfaces (mapcat class:interfaces bases)]
     (set (concat bases interfaces)))))
link
(ancestor:all String) => #{java.lang.CharSequence java.lang.constant.ConstantDesc java.lang.constant.Constable java.lang.Comparable java.lang.String java.io.Serializable java.lang.Object}

ancestor:list ^

[cls] [cls output]
Added 3.0

lists the direct ancestors of a class

v 3.0
(defn ancestor:list
  ([cls] (ancestor:list cls []))
  ([^java.lang.Class cls output]
   (if (nil? cls)
     output
     (recur (.getSuperclass cls) (conj output cls)))))
link
(ancestor:list clojure.lang.PersistentHashMap) => [clojure.lang.PersistentHashMap clojure.lang.APersistentMap clojure.lang.AFn java.lang.Object]

ancestor:tree ^

[cls] [cls output]
Added 3.0

lists the hierarchy of bases and interfaces of a class.

v 3.0
(defn ancestor:tree
  ([cls] (ancestor:tree cls []))
  ([^Class cls output]
   (let [base (.getSuperclass cls)]
     (if-not base output
             (recur base
                    (conj output [base (class:interfaces cls)]))))))
link
(ancestor:tree Class) => [[java.lang.Object #{java.lang.constant.Constable java.lang.invoke.TypeDescriptor java.lang.reflect.GenericDeclaration java.io.Serializable java.lang.reflect.Type java.lang.reflect.AnnotatedElement java.lang.invoke.TypeDescriptor$OfField}]] (ancestor:tree clojure.lang.PersistentHashMap) => [[clojure.lang.APersistentMap #{clojure.lang.IMapIterable clojure.lang.IMeta clojure.lang.IKVReduce clojure.lang.IObj clojure.lang.IEditableCollection}] [clojure.lang.AFn #{java.lang.Iterable clojure.lang.ILookup clojure.lang.MapEquivalence java.io.Serializable clojure.lang.IPersistentMap clojure.lang.IPersistentCollection java.util.Map clojure.lang.Counted clojure.lang.Associative clojure.lang.IHashEq clojure.lang.Seqable}] [java.lang.Object #{java.lang.Runnable java.util.concurrent.Callable clojure.lang.IFn}]]

class:abstract? ^

[class]
Added 3.0

returns `true` if `class` is an abstract class

v 3.0
(defn class:abstract?
  ([^Class class]
   (java.lang.reflect.Modifier/isAbstract (.getModifiers class))))
link
(class:abstract? java.util.Map) => true (class:abstract? Class) => false

class:array-component ^

[cls]
Added 3.0

returns the array element within the array

v 3.0
(defn class:array-component
  ([^Class cls]
   (if (class:array? cls)
     (.getComponentType cls)
     (throw (ex-info "Not an array" {:class cls})))))
link
(class:array-component (type (int-array 0))) => Integer/TYPE (class:array-component (type (into-array [1 2 3]))) => java.lang.Long

class:array? ^

[cls]
Added 3.0

checks if a class is an array class

v 3.0
(defn class:array?
  ([^Class cls]
   (.isArray cls)))
link
(class:array? (type (int-array 0))) => true

class:inherits? ^

[ancestor cls]
Added 3.0

checks if one class inherits from another

v 3.0
(defn class:inherits?
  ([ancestor cls]
   (contains? (ancestor:all cls) ancestor)))
link
(class:inherits? clojure.lang.ILookup clojure.lang.APersistentMap) => true

class:interface? ^

[class]
Added 3.0

returns `true` if `class` is an interface

v 3.0
(defn class:interface?
  ([^Class class]
   (.isInterface class)))
link
(class:interface? java.util.Map) => true (class:interface? Class) => false

class:interfaces ^

[cls]
Added 3.0

lists all interfaces for a class

v 3.0
(defn class:interfaces
  ([^Class cls]
   (let [directs (.getInterfaces cls)
         sub (vec (mapcat class:interfaces directs))]
     (set (concat directs sub)))))
link
(class:interfaces clojure.lang.IFn) => #{java.lang.Runnable java.util.concurrent.Callable} (class:interfaces clojure.lang.AFn) => #{java.lang.Runnable java.util.concurrent.Callable clojure.lang.IFn}

class:match ^

[candidates cls]
Added 3.0

finds the best matching interface or class from a list of candidates

v 3.0
(defn class:match
  ([candidates ^Class cls]
   (or (get candidates cls)
       (->> (apply concat (ancestor:tree cls))
            (map (fn [v]
                   (if (set? v)
                     (first (set/intersection v candidates))
                     (get candidates v))))
            (filter identity)
            first))))
link
(class:match #{Object} Long) => Object (class:match #{String} Long) => nil (class:match #{Object Number} Long) => Number

create-lookup ^

[m] [m ignore]
Added 3.0

creates a path lookup given a record

v 3.0
(defn create-lookup
  ([m] (create-lookup m (constantly false)))
  ([m ignore]
   (reduce-kv (fn [out type record]
                (reduce-kv (fn [out k v]
                             (if (ignore k)
                               out
                               (assoc out v [type k])))
                           out
                           record))
              {}
              m)))
link
(create-lookup {:byte {:name "byte" :size 1} :long {:name "long" :size 4}}) => {"byte" [:byte :name] 1 [:byte :size] "long" [:long :name] 4 [:long :size]}

primitive ^

[v to]
Added 3.0

converts primitive values across their different representations. The choices are: :raw - The string in the jdk (i.e. `Z` for Boolean, `C` for Character) :symbol - The symbol that std.object.query uses for matching (i.e. boolean, char, int) :string - The string that std.object.query uses for matching :class - The primitive class representation of the primitive :container - The containing class representation for the primitive type

v 3.0
(defn primitive
  ([v to]
   (if-let [[type rep] (get +primitive-lookup+ v)]
     (get-in +primitive-records+ [type to]))))
link
(primitive Boolean/TYPE :symbol) => 'boolean (primitive "Z" :symbol) => 'boolean (primitive "int" :symbol) => 'int (primitive Character :string) => "char" (primitive "V" :class) => Void/TYPE (primitive 'long :container) => Long (primitive 'long :type) => :long

primitive:array? ^

[cls]
Added 3.0

checks if class is a primitive array

v 3.0
(defn primitive:array?
  ([^Class cls]
   (and (.isArray cls)
        (.isPrimitive (.getComponentType cls)))))
link
(primitive:array? (type (int-array 0))) => true (primitive:array? (type (into-array [1 2 3]))) => false

primitive? ^

[cls]
Added 3.0

checks if a class of primitive type

v 3.0
(defn primitive?
  ([^Class cls]
   (.isPrimitive cls)))
link
(primitive? Integer/TYPE) => true (primitive? Integer) => false

4    std.lib.class: A Comprehensive Summary

The std.lib.class namespace provides a collection of utility functions for introspecting and manipulating Java classes and their hierarchies within Clojure. It offers tools to query class properties, determine inheritance relationships, and work with primitive types and arrays. This module is fundamental for tasks involving reflection, type checking, and dynamic class analysis.

4.1    Core Concepts:

  • Class Introspection: Functions to examine various characteristics of a java.lang.Class object, such as whether it's an array, a primitive, an interface, or an abstract class.n Type Hierarchy Traversal: Utilities to list ancestors (superclasses and interfaces) of a given class, providing a comprehensive view of its type hierarchy.n Primitive Type Mapping: A robust system for mapping between different representations of Java primitive types (e.g., keyword, symbol, Class object, internal JVM raw type string).n* Class Matching: A function to find the "best match" for a given class from a set of candidate classes or interfaces, useful for type resolution.

4.2    Key Functions:

  • class:array?:n Purpose: Checks if a given Class object represents an array type.n Usage: (class:array? (type (int-array 0)))n primitive?:n Purpose: Checks if a given Class object represents a primitive Java type (e.g., int, boolean).n Usage: (primitive? Integer/TYPE)n primitive:array?:n Purpose: Checks if a given Class object represents an array of primitive types.n Usage: (primitive:array? (type (int-array 0)))n class:array-component:n Purpose: Returns the Class object representing the component type of an array class.n Usage: (class:array-component (type (int-array 0)))n class:interface?:n Purpose: Checks if a given Class object represents an interface.n Usage: (class:interface? java.util.Map)n class:abstract?:n Purpose: Checks if a given Class object represents an abstract class.n Usage: (class:abstract? java.util.AbstractMap)n primitive:n Purpose: A powerful lookup function that converts between various representations of primitive types. It can take a primitive's keyword, symbol, raw JVM string, or Class object and return its corresponding :raw, :symbol, :string, :class, or :container representation.n Usage: (primitive Boolean/TYPE :symbol), (primitive "Z" :class)n create-lookup:n Purpose: A helper function used internally to build efficient lookup maps for primitive type conversions.n ancestor:list:n Purpose: Returns a list of all direct superclasses (ancestors) of a given class, starting from the class itself up to java.lang.Object.n Usage: (ancestor:list clojure.lang.PersistentHashMap)n class:interfaces:n Purpose: Returns a set of all interfaces implemented by a given class, including interfaces inherited from superclasses.n Usage: (class:interfaces clojure.lang.AFn)n ancestor:tree:n Purpose: Returns a detailed hierarchy of a class, including its superclasses and the interfaces implemented at each level.n Usage: (ancestor:tree Class)n ancestor:all:n Purpose: Returns a set of all classes and interfaces that a given class inherits from or implements, including the class itself.n Usage: (ancestor:all String)n class:inherits?:n Purpose: Checks if one class (the cls) inherits from or implements another class or interface (the ancestor).n Usage: (class:inherits? clojure.lang.ILookup clojure.lang.APersistentMap)n class:match:n Purpose: Given a set of candidate classes/interfaces and a target class, it finds the "best" matching candidate in the target class's hierarchy. "Best" typically means the most specific common ancestor.n Usage: (class:match #{Object Number} Long)

4.3    Usage Pattern:

This namespace is crucial for:

  • Dynamic Type Checking: Determining the exact nature of an object's class at runtime.n Reflection-based Operations: Building tools that need to understand and navigate class hierarchies.n Code Generation/Metaprogramming: When generating code that interacts with specific Java types or needs to ensure type compatibility.n* Library Development: Providing robust type-related utilities for other parts of the foundation-base ecosystem.

By centralizing these class-related utilities, std.lib.class promotes consistent and reliable handling of Java types within Clojure applications.