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) ; false
Access a public field of an object:
(def rect (java.awt.Rectangle. 0 0 3 4))
(.-width rect) ; 3
(.-height rect) ; 4
Import 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 deps
Use 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"
.