Interoperating with Java
Instantiate a Java class, thereby invoking its constructor:
(def songs (java.io.File. "songs.txt"))Call a method on an object using . notation:
(.exists songs) ; falseAccess a public field of an object:
(def rect (java.awt.Rectangle. 0 0 3 4))
(.-width rect) ; 3
(.-height rect) ; 4Import a Java package from the REPL:
(import java.io.File)Import multiple classes from the same Java from the REPL:
(import '(java.io File InputStream))Import a Java package from a source file:
(ns interop.core
(:import java.io.File))Import multiple classes from the same Java from a source file:
(ns interop.core
(:import (java.io File InputStream)))Use the imported class without package qualification:
(def albums (File. "albums.txt"))The java.lang package, providing String, Boolean, etc., is
imported automatically.
Access static fields and methods using / notation:
(interpose File/separator ["home" "patrick" "clojure"])
(String/format "%s%s%s.%s" (to-array ["docs" File/separator "diary" "txt"]))
;; "docs/diary.txt"The to-array function turns a seqable into a Java array.
Add a Java library (e.g. Gson) to the Leiningen project’s dependencies
in project.clj:
:dependencies [[org.clojure/clojure "1.11.1"]
[com.google.code.gson/gson "2.11.0"]]Install added dependencies on the shell:
lein depsUse Gson to output values as JSON:
(import com.google.gson.Gson)
(def gson (Gson.))
(println (.toJson gson {:name "Java" :creator "James Gosling" :properties ["verbose" "big"]}))
;; {":name":"Java",":creator":"James Gosling",":properties":["verbose","big"]}Turn a method into a function using memfn:
(def file-names ["albums.txt" "songs.txt" "bands.txt"])
(def files (map #(File. %) file-names))
(map (memfn exists) files) ; (false false false)Java objects are mutable:
(def songs (java.util.ArrayList.))
(.add songs "Pale Fire")
(.add songs "Monument")
(map str songs) ; ("Pale Fire" "Monument")Exercises
Rectangle Enclosure
Write a function rect that expects a map with the keys :x, :y,
:w, and :h that describe the a rectangle’s upper-left corner x/y
coordinates as well as its width and height, and returns a
java.awt.Rectangle instance.
Write a function encloses? that expects two java.awt.Rectangle
instances as arguments and returns true if the first rectangle
completely encloses the second rectangle, and false otherwise.
Hint: Use map key destructuring in the rect function. The first two
constructor arguments describe the x and y position of the
rectangle’s upper-left corner. Access the relevant properties using
.-x, .-y, .-width, and .-height.
Test: (encloses? (rect {:x 3 :y 4 :w 5 :h 6}) (rect {:x 4 :y 5 :w 1 :h 2})) shall return true, and (encloses? (rect {:x 3 :y 4 :w 5 :h 6}) (rect {:x 4 :y 5 :w 10 :h 2})) shall return false.
String Concatenation
Write a function concatenate that returns a string of the
concatenated items of the seqable given as an argument. The
concatenation shall be performed in-place using the
java.util.StringBuilder class.
Hint: Use the .toString method to turn the individual items into
strings.
Test: (concatenate [1 "2" 3 "456" 7 \8 "9"]) shall return "123456789".
CamelCase
Write a function camel-case that converts Clojure identifiers to CamelCase.
Hint: Use the class
CaseUtils
from the Apache Commons Text package
org.apache.commons.commons-text. Use char-array to convert a
seqable of characters to a Java array (required to pass varargs).
Test: (camel-case "private-field-accessor-util") shall return "PrivateFieldAccessorUtil".