Understand Clojure character literals, boolean values, truthiness, nil, predicates, and conditional forms through the differences Java developers notice first.
Characters and booleans look simple, but Java developers usually hit two Clojure-specific differences quickly:
false and nil are falsey.Those two details affect parsing, validation, and every if, when, and cond you write.
Clojure characters are JVM Character values:
1(class \a)
2;; => java.lang.Character
| Clojure | Meaning | Java comparison |
|---|---|---|
\a |
The character a |
'a' |
\space |
Space character | ' ' |
\newline |
Newline character | '\n' |
\tab |
Tab character | '\t' |
\u03BB |
Unicode lambda | '\u03BB' |
Use char? to test for a character:
1(char? \a)
2;; => true
3
4(char? "a")
5;; => false
Convert between characters and code points deliberately:
1(int \A)
2;; => 65
3
4(char 65)
5;; => \A
int returns the numeric value of the JVM character code unit. Do not describe this as “ASCII” in general; Clojure strings and characters are Unicode-aware because they sit on JVM strings and chars.
Clojure has true and false:
1(class true)
2;; => java.lang.Boolean
Predicates conventionally end with ?:
1(require '[clojure.string :as str])
2
3(number? 42)
4(string? "hello")
5(nil? nil)
6(str/blank? " ")
That naming convention is not enforced by the compiler, but it is strong Clojure style. Java developers should read a trailing ? as “this function returns a truthy or falsey answer.”
This is the important rule:
| Value | Truthy in Clojure? |
|---|---|
false |
no |
nil |
no |
true |
yes |
0 |
yes |
"" |
yes |
[] |
yes |
{} |
yes |
() |
yes |
Java developers often expect 0, empty strings, or empty collections to behave like false. They do not.
1(if 0
2 :truthy
3 :falsey)
4;; => :truthy
5
6(if []
7 :truthy
8 :falsey)
9;; => :truthy
Use explicit predicates when emptiness matters:
1(if (seq items)
2 :has-items
3 :empty)
seq returns a sequence when a collection has elements and nil when it is empty, which makes it useful in conditionals.
| Form | Use when | Java comparison |
|---|---|---|
if |
You need true and false branches | if/else, but expression-oriented |
when |
You only need the true branch | if without else |
when-not |
You only need the falsey case | Negated if |
cond |
You need ordered cases | if/else if chain |
case |
You need constant dispatch by value | switch-like |
Examples:
1(defn display-name [user]
2 (if-let [name (:name user)]
3 name
4 "Anonymous"))
if-let binds a value only when it is truthy. This is common for optional map values:
1(defn classify-score [score]
2 (cond
3 (nil? score) :missing
4 (>= score 90) :excellent
5 (>= score 70) :passing
6 :else :needs-review))
Clojure uses nil; Java APIs use null. At interop boundaries, treat them as the same absence signal but do not let absence leak unchecked into core logic:
1(defn safe-length [s]
2 (if (nil? s)
3 0
4 (.length ^String s)))
In pure Clojure code, prefer returning nil for absence and using predicates or some-> to handle optional values:
1(some-> user
2 :email
3 str/lower-case)
(if 0 :yes :no), (if "" :yes :no), and (if [] :yes :no).if (list.isEmpty()) check using seq or empty?.initial that returns the first character of a nonblank string or nil.safe-active? that treats missing :active? as false.\a, \space, and \newline.false and nil are falsey in Clojure.empty?, seq, nil?, and str/blank? when you need a specific condition.null appears at interop boundaries; normalize absence before it spreads.