Dynamic typing is cool, but sometimes you just want to know the type of something.
I’ve seen people write this:
(isa? (type x) SomeJavaClass)
As its docstring describes, isa?
checks inheritance relationships, which may come from either Java class inheritance or Clojure hierarchies.
isa?
manually walks the class inheritance tree, and has special cases for vectors of types to support multiple-argument dispatch in multimethods.
;; isa? with a vector of types. ;; Both sequences and vectors are java.util.List. (isa? [(type (range)) (type [1 2 3])] [java.util.List java.util.List]) ;;=> true
Hierarchies are an interesting but rarely-used feature of Clojure.
(derive java.lang.String ::immutable) (isa? (type "Hello") ::immutable) ;;=> true
If all you want to know is “Is x a Foo?” where Foo is a Java type, then (instance? Foo x)
is simpler and faster than isa?
.
Some examples:
(instance? String "hello") ;;=> true (instance? Double 3.14) ;;=> true (instance? Number 3.14) ;;=> true (instance? java.util.Date #inst "2015-01-01") ;;=> true
Note that instance?
takes the type first, opposite to the argument order of isa?
. This works nicely with condp
:
(defn make-bigger [x] (condp instance? x String (clojure.string/upper-case x) Number (* x 1000))) (make-bigger 42) ;;=> 42000 (make-bigger "Hi there") ;;=> "HI THERE"
instance?
maps directly to Java’s Class.isInstance(Object). It works for both classes and interfaces, but does not accept nil
as a type.
(isa? String nil) ;;=> false (instance? String nil) ;;=> false (isa? nil nil) ;;=> true (instance? nil nil) ;; NullPointerException
Remember that defrecord
and deftype
produce Java classes as well:
(defrecord FooBar [a]) (instance? FooBar (->FooBar 42)) ;;=> true
Remember also that records and types are classes, not Vars, so to reference them from another namespace you must :import
instead of :require
them.
instance?
won’t work correctly with Clojure protocols. To check if something supports a protocol, use satisfies?
.