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+
- +primitive-records+
- +primitives+
- ancestor:all
- ancestor:list
- ancestor:tree
- class:abstract?
- class:array-component
- class:array?
- class:inherits?
- class:interface?
- class:interfaces
- class:match
- create-lookup
- primitive
- primitive:array?
- primitive?
+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
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}
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]
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}]]
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
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
v 3.0
(defn class:array?
([^Class cls]
(.isArray cls)))
link
(class:array? (type (int-array 0))) => true
v 3.0
(defn class:inherits?
([ancestor cls]
(contains? (ancestor:all cls) ancestor)))
link
(class:inherits? clojure.lang.ILookup clojure.lang.APersistentMap) => true
v 3.0
(defn class:interface?
([^Class class]
(.isInterface class)))
link
(class:interface? java.util.Map) => true (class:interface? Class) => false
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]
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
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]
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
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.Classobject, 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,Classobject, 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 givenClassobject represents an array type.n Usage:(class:array? (type (int-array 0)))nprimitive?:n Purpose: Checks if a givenClassobject represents a primitive Java type (e.g.,int,boolean).n Usage:(primitive? Integer/TYPE)nprimitive:array?:n Purpose: Checks if a givenClassobject represents an array of primitive types.n Usage:(primitive:array? (type (int-array 0)))nclass:array-component:n Purpose: Returns theClassobject representing the component type of an array class.n Usage:(class:array-component (type (int-array 0)))nclass:interface?:n Purpose: Checks if a givenClassobject represents an interface.n Usage:(class:interface? java.util.Map)nclass:abstract?:n Purpose: Checks if a givenClassobject represents an abstract class.n Usage:(class:abstract? java.util.AbstractMap)nprimitive: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, orClassobject and return its corresponding:raw,:symbol,:string,:class, or:containerrepresentation.n Usage:(primitive Boolean/TYPE :symbol),(primitive "Z" :class)ncreate-lookup:n Purpose: A helper function used internally to build efficient lookup maps for primitive type conversions.nancestor:list:n Purpose: Returns a list of all direct superclasses (ancestors) of a given class, starting from the class itself up tojava.lang.Object.n Usage:(ancestor:list clojure.lang.PersistentHashMap)nclass: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)nancestor: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)nancestor: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)nclass:inherits?:n Purpose: Checks if one class (thecls) inherits from or implements another class or interface (theancestor).n Usage:(class:inherits? clojure.lang.ILookup clojure.lang.APersistentMap)nclass: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-baseecosystem.
By centralizing these class-related utilities, std.lib.class promotes consistent and reliable handling of Java types within Clojure applications.