Search icon
Arrow left icon
All Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Newsletters
Free Learning
Arrow right icon
Clojure for Java Developers

You're reading from  Clojure for Java Developers

Product type Book
Published in Feb 2016
Publisher
ISBN-13 9781785281501
Pages 156 pages
Edition 1st Edition
Languages

Chapter 3. Interacting with Java

We know a bit about how to organize our code and how that relates to packages in Java. Now, you surely need to use your old Java code and all the libraries you already know; Clojure encourages a new way to think about programming and it also allows you to use all the dependencies and code that you've already generated.

Clojure is a Java Virtual Machine (JVM) language and as such it is compatible with most Java dependencies and libraries out there; you should be able to use all the tools out there. You should also be able to use your Clojure programs with Java-only programs, this requires a bit of custom coding but in the end you can use Clojure in the right places of your project.

To be able to do this, we'll have to learn:

  • Using Maven dependencies

  • Using plain old Java classes from your Clojure code base

  • A bit more about the Clojure language, in particular the let statements and destructuring

  • Creating a Java interface for your Clojure code

  • Using the Java interface...

Using Maven dependencies


Let's say that we want to write an image manipulation program; it is a very simple program that should be able to create thumbnails. Most of our codebase is in Clojure, so we want to write this in Clojure too.

There are a bunch of Java libraries meant to manipulate images, we decide to use imgscalr, which is very simple to use and it looks like it is available in Maven Central (http://search.maven.org/).

Let's create a new Leiningen project, as shown:

lein new thumbnails

Now, we need to edit the project.clj file in the thumbnails project:

(defproject thumbnails "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "Eclipse Public License"
            :url "http://www.eclipse.org/legal/epl-v10.html"}
  :dependencies [[org.clojure/clojure "1.6.0"]])

You can add the imgscalr dependency similar to the following code:

(defproject thumbnails "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example...

Clojure interop syntax


Clojure was designed to be a Hosted Language, which means that it can run in different environments or runtimes. One important philosophy aspect is that Clojure does not attempt to get in the way of your original host; this allows you to use your knowledge of the underlying platform to your advantage.

In this case, we are using the Java platform. Let's look at the basic interrupt syntax that we need to know.

Creating an object

There are two ways to create an object in Clojure; for example, let's have a look at how to create an instance of java.util.ArrayList.

(def a (new java.util.ArrayList 20))

Here, we are using the new special form, as you can see it receives a symbol (the name of the class java.util.ArrayList) and in this case it is an integer.

The symbol java.util.ArrayList represents the classname and any Java class name will do here.

Next, you can actually pass any number of parameters (including 0 parameters). The next parameters are the parameters of the constructor...

Writing a simple image namespace


Let's now write some Clojure code and create a file in src/thumbnails/image.clj.

Let's try to do this the Clojure way. First of all, write the namespace declaration and evaluate it:

(ns thumbnails.image
  (:require [clojure.java.io :as io])
  (:import [javax.imageio ImageIO]
           [java.awt.image BufferedImageOp]
           [org.imgscalr Scalr Scalr$Mode]))

Now open up a REPL and write the following code:

(def image-stream (io/input-stream "http://imgs.xkcd.com/comics/angular_momentum.jpg"))(def image (ImageIO/read image-stream))
image
(.getWidth image)

We now have an image instance and you can call all of the Java methods in the REPL. This is one of Clojure's core concepts, you can play with the REPL and check your code before really writing it and you can do it in an interactive way, as shown:

In the end, we want to stick with the following contents:

(ns thumbnails.image
  (:require [clojure.java.io :as io])
  (:import [javax.imageio ImageIO]
           ...

Writing the tests


Now that you have written your image processing code, it is a good time to write the tests.

Let's just check if we can generate a thumbnail. Create a new thumbnails.thumbnail-test namespace, in the tests.

Remember, if you create the file, it must be named test/thumbnails/thumbnail_test.clj.

Add the following contents to it:

(ns thumbnails.thumbnail-test
  (:require [clojure.test :refer :all]
            [clojure.java.io :as io]
            [thumbnails.image :refer :all]))

(deftest test-image-width
  (testing "We should be able to get the image with"
    (let [image-stream (io/input-stream "http://imgs.xkcd.com/comics/angular_momentum.jpg")
          image (load-image image-stream)]
      (save-image image "xkcd-width.png")
      (is (= 600 (get-image-width (io/input-stream "xkcd-width.png")))))))

(deftest test-load-image
  (testing "We should be able to generate thumbnails"
    (let [image-stream (io/input-stream "http://imgs.xkcd.com/comics/angular_momentum.jpg")
      ...

Destructuring in Clojure


Destructuring is a feature in Clojure that is not common in other lisps; the idea is to allow you to write more concise code in scenarios where code doesn't really add value (for example, getting the first element from a list or the second parameter from a function) and concentrating only on what is important to you.

In order to understand this better, let's see an example of why destructuring can help you:

(let [v [1 2 3]] [(first v) (nth v 2)]) ;; [1 3]

What's wrong with the previous code? Nothing really, but you need to start thinking about what is v, what the first value of v is, what the nth function does, and at what index v starts.

Instead we can do this:

(let [[f s t] [1 2 3]] [f t]) ;; [1 3]

Once you are used to destructuring, you will see that you don't need to think about how to get the elements you need. In this case, we directly access the first, second, and third elements from our vector and use the first and third out of the three elements. With good naming...

Exposing your code to Java


If you want to be able to use Clojure code from other JVM languages, in Clojure, there are a couple of ways in which you can do it:

  • You can generate new Java classes and use them as you normally would; it can implement some interface or extend from some other class

  • You can generate a proxy on the fly, this way you can implement a contract (in the form of a class or an interface) that some framework requires with little code and effort

  • You can use the clojure.java.api package to call Clojure functions directly from Java

Note

You can find more information on how this works at the following location: http://www.falkoriemenschneider.de/a__2014-03-22__Add-Awesomeness-to-your-Legacy-Java.html.

Let's have a look at how we can define a Java class.

Create a new namespace called thumbnails.image-java and write the following code:

(ns thumbnails.image-java
  (:require [thumbnails.image :as img])
  (:gen-class
    :methods [[loadImage [java.io.InputStream] java.awt.image.BufferedImage...

Proxy and reify


There are situations when you are interacting with Java libraries, where you must send an instance of a specific Java class to some method; writing a class isn't the best option, you should rather create an instance that conforms to a contract expected by some framework on the fly. We have two options to do this:

  • Proxy: It allows you to implement a Java interface or extend from some super class. In reality, it creates a new object that calls your Clojure functions when needed

  • Reify: Reify allows you to implement interfaces and Clojure protocols (we will see them later). It is not capable of extending classes. It is a better performant than the proxy and should be used whenever possible.

Let's look at a minimal example:

(import '(javax.swing JFrame JLabel JTextField JButton)
        '(java.awt.event ActionListener)
        '(java.awt GridLayout))
(defn sample []
  (let [frame (JFrame. "Simple Java Integration")
        sample-button (JButton. "Hello")]
    (.addActionListener...

Summary


In this chapter, you have gained a lot of power from Clojure with a few new primitives.

As you can see, there are plenty of ways to interact with your current codebase; specifically, you can now:

  • Use Java code from Clojure

  • Use Clojure code from Java

  • Reuse Java frameworks by creating objects that adhere to their contracts

With all of our new tools in mind, we are ready to tackle more concepts and a little bit more complexity with collections and data structures.

lock icon The rest of the chapter is locked
You have been reading a chapter from
Clojure for Java Developers
Published in: Feb 2016 Publisher: ISBN-13: 9781785281501
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $15.99/month. Cancel anytime}