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
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 Valuesstr 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.
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.
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.
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.
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.
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.
slugify that trims, lowercases, replaces whitespace with -, and removes non-word characters.->> pipeline.nil, "", and whitespace-only values are rejected differently if your domain needs different messages.(str nil) with (str/blank? nil) and decide which behavior belongs in validation code.java.lang.String values.clojure.string for readable pipelines.nil, empty strings, and blank strings as separate validation concerns.