Browse Learn Clojure Foundations as a Java Developer

Strings in Clojure for Java Developers

Work with Clojure strings as JVM strings: immutable text values, clojure.string functions, regex operations, formatting, nil handling, and Java interop boundaries.

Clojure strings are Java strings. A Clojure string literal produces a java.lang.String, and Java string methods are available through interop. The difference is style: Clojure code usually treats strings as values flowing through functions rather than as objects receiving method calls everywhere.

1(class "hello")
2;; => java.lang.String

Common Operations

Require clojure.string with a short alias:

1(require '[clojure.string :as str])
Task Clojure Java mental model
Concatenate values (str "Hello, " name) + or StringBuilder
Format text (format "Hello, %s" name) String.format
Trim whitespace (str/trim value) value.trim()
Lowercase (str/lower-case value) value.toLowerCase(...)
Split (str/split value #",") value.split(",")
Join (str/join ", " values) String.join(", ", values)
Replace (str/replace value #"\\s+" "-") replaceAll or replace
Substring (subs value 0 5) value.substring(0, 5)

The function style composes well with pipelines:

1(defn normalize-tag [s]
2  (-> s
3      str/trim
4      str/lower-case
5      (str/replace #"\s+" "-")))
6
7(normalize-tag "  Java Interop  ")
8;; => "java-interop"

str Converts Values

str is not just concatenation. It converts values to text:

1(str "order-" 42)
2;; => "order-42"
3
4(str :status)
5;; => ":status"

Be careful with nil:

1(str nil)
2;; => ""

That is convenient for presentation but dangerous if nil means missing data. At validation boundaries, check for missing values before converting.

Formatting

format delegates to Java’s formatter style:

1(format "%s owes %.2f" "Ada" 10.5)
2;; => "Ada owes 10.50"

Use format when positional formatting matters. Use str or a small helper when building simple identifiers.

Regex and Replacement

Clojure regex literals use #"":

1(re-find #"\d+" "ticket-123")
2;; => "123"
3
4(str/replace "a   b\tc" #"\s+" " ")
5;; => "a b c"

clojure.string/split uses regular expressions, not literal delimiters:

1(str/split "a.b.c" #"\.")
2;; => ["a" "b" "c"]

That difference matters for Java developers because "." as a regex means “any character.” Escape regex metacharacters when needed.

Interop With Java String Methods

You can call Java methods directly:

1(.startsWith "clojure" "clj")
2;; => false
3
4(.contains "clojure" "oj")
5;; => true

Prefer clojure.string functions when they read better in pipelines. Use Java methods when the method is clearer, more direct, or required by interop.

Blank, Empty, and Nil

Text validation often needs three different checks:

Value (nil? value) (empty? value) (str/blank? value)
nil true false true
"" false true true
" " false false true
"x" false false false

Example:

1(defn required-text [value]
2  (when-not (str/blank? value)
3    (str/trim value)))

This returns nil for missing or blank values and a trimmed string otherwise.

String Pipelines

A common Java loop:

1List<String> normalized = new ArrayList<>();
2
3for (String tag : tags) {
4    if (!tag.isBlank()) {
5        normalized.add(tag.trim().toLowerCase(Locale.ROOT));
6    }
7}

Clojure version:

1(defn normalize-tags [tags]
2  (->> tags
3       (remove str/blank?)
4       (map str/trim)
5       (map str/lower-case)
6       vec))

The result is a new vector. No caller-visible collection is mutated.

Practice

  1. Write slugify that trims, lowercases, replaces whitespace with -, and removes non-word characters.
  2. Rewrite a Java string loop as a Clojure ->> pipeline.
  3. Add validation so nil, "", and whitespace-only values are rejected differently if your domain needs different messages.
  4. Compare (str nil) with (str/blank? nil) and decide which behavior belongs in validation code.

Key Takeaways

  • Clojure strings are java.lang.String values.
  • Use clojure.string for readable pipelines.
  • Remember that regex functions use regex semantics, not plain delimiters.
  • Treat nil, empty strings, and blank strings as separate validation concerns.
  • Use Java string methods where interop or clarity makes them the best fit.

Quiz: Strings in Clojure

### What class backs a Clojure string literal? - [x] `java.lang.String` - [ ] `clojure.lang.Symbol` - [ ] `java.lang.StringBuilder` - [ ] `clojure.lang.Keyword` > **Explanation:** Clojure string literals are JVM `String` values. ### What does `(str nil)` return? - [x] The empty string - [ ] `nil` - [ ] `"nil"` - [ ] An exception > **Explanation:** `str` converts `nil` to the empty string, which is convenient but can hide missing data if used too early. ### Which namespace provides `trim`, `split`, `join`, and `blank?`? - [x] `clojure.string` - [ ] `clojure.set` - [ ] `clojure.java.io` - [ ] `clojure.edn` > **Explanation:** `clojure.string` contains common string helpers. ### Why does `(str/split "a.b.c" #"\.")` escape the dot? - [x] Because split uses a regular expression and dot is a regex metacharacter. - [ ] Because dots are illegal in Clojure strings. - [ ] Because Java strings cannot contain dots. - [ ] Because `split` only accepts keywords. > **Explanation:** Regex metacharacters must be escaped when you mean the literal character. ### True or False: Prefer lazy string pipelines for hidden side effects. - [ ] True - [x] False > **Explanation:** String pipelines should produce values. Use explicit side-effect forms at boundaries.
Revised on Saturday, May 23, 2026