617 lines
42 KiB
HTML
617 lines
42 KiB
HTML
<!DOCTYPE html>
|
|
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
|
<head>
|
|
<meta charset="utf-8"/>
|
|
<title>Clojure Guides: Clojure interoperability with Java</title>
|
|
|
|
|
|
<meta name="description" content="This guide covers:">
|
|
|
|
<meta property="og:description" content="This guide covers:">
|
|
|
|
<meta property="og:url" content="https://clojure-doc.github.io/articles/language/interop/" />
|
|
<meta property="og:title" content="Clojure interoperability with Java" />
|
|
<meta property="og:type" content="article" />
|
|
|
|
<link rel="canonical" href="https://clojure-doc.github.io/articles/language/interop/">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<link href="https://fonts.googleapis.com/css?family=Alegreya:400italic,700italic,400,700" rel="stylesheet"
|
|
type="text/css">
|
|
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css">
|
|
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css" rel="stylesheet">
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.7.0/styles/default.min.css">
|
|
<link href="../../../css/screen.css" rel="stylesheet" type="text/css" />
|
|
</head>
|
|
<body>
|
|
|
|
|
|
<nav class="navbar navbar-default">
|
|
<div class="container">
|
|
<div class="navbar-header">
|
|
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
|
|
<span class="sr-only">Toggle navigation</span>
|
|
<span class="icon-bar"></span>
|
|
<span class="icon-bar"></span>
|
|
<span class="icon-bar"></span>
|
|
</button>
|
|
<a class="navbar-brand" href="../../../index.html">Clojure Guides</a>
|
|
</div>
|
|
<div id="navbar" class="navbar-collapse collapse">
|
|
<ul class="nav navbar-nav navbar-right">
|
|
<li ><a href="../../../index.html">Home</a></li>
|
|
<li><a href="https://github.com/clojure-doc/clojure-doc.github.io">Contribute</a></li>
|
|
</ul>
|
|
</div><!--/.nav-collapse -->
|
|
</div><!--/.container-fluid -->
|
|
</nav>
|
|
|
|
|
|
<div class="container">
|
|
|
|
|
|
<div class="row">
|
|
<div class="col-lg-9">
|
|
<div id="content">
|
|
|
|
<div id="custom-page">
|
|
<div id="page-header">
|
|
<h2>Clojure interoperability with Java</h2>
|
|
</div>
|
|
|
|
<p>This guide covers:</p><ul><li>How to instantiate Java classes</li><li>How to invoke Java methods</li><li>How to extend Java classes with proxy</li><li>How to implement Java interfaces with reify</li><li>How to generate Java classes with gen-class</li><li>Other topics related to interop</li></ul><p>This work is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by/3.0/">Creative Commons Attribution 3.0 Unported License</a>
|
|
(including images & stylesheets). The source is available <a href="https://github.com/clojure-doc/clojure-doc.github.io">on Github</a>.</p><h2 id="what-version-of-clojure-does-this-guide-cover">What Version of Clojure Does This Guide Cover?</h2><p>This guide covers Clojure 1.5.</p><h2 id="overview">Overview</h2><p>Clojure was designed to be a hosted language that directly interoperates with its host platform (JVM, CLR and so on).
|
|
Clojure code is compiled to JVM bytecode. For method calls on Java objects, Clojure compiler
|
|
will try to emit the same bytecode <code>javac</code> would produce.</p><p>It is possible to implement interfaces, extend and generate Java classes in Clojure.</p><p>Clojure also provides convenient functions and macros that make consuming of Java libraries
|
|
easier and often more concise than it would be in Java code.</p><h2 id="imports">Imports</h2><p>Java classes can be referenced either using their fully-qualified names (FQNs) such as
|
|
<code>java.util.Date</code> or be <em>imported</em> in the current Clojure namespace using <code>clojure.core/import</code> and
|
|
referenced by short names:</p><pre><code class="clojure">java.util.Date ; ⇒ java.util.Date
|
|
</code></pre><pre><code class="clojure">(import java.util.Date)
|
|
|
|
Date ; ⇒ java.util.Date
|
|
</code></pre><p><code>ns</code> macro supports imports, too:</p><pre><code class="clojure">(ns myservice.main
|
|
(:import java.util.Date))
|
|
</code></pre><p>More about the <code>ns</code> macro can be found in the article on <a href="../namespaces/index.html">Clojure namespaces</a>.</p><p>Dynamic (at runtime) imports are usually only used in the REPL and cases when there are multiple implementations of a particular
|
|
protocol/service/feature and it is not possible to tell which one should be used until run time.</p><h3 id="automatic-imports-for-javalang">Automatic Imports For java.lang.*</h3><p>Classes from the <code>java.lang</code> package are automatically imported. For example, you can use <code>String</code> or <code>Math</code>
|
|
without explicitly importing them:</p><pre><code class="clojure">(defn http-uri?
|
|
[^String uri]
|
|
(.startsWith (.toLowerCase uri) "http"))
|
|
|
|
(Math/round 0.7886)
|
|
</code></pre><h3 id="inner-nested-classes">Inner (Nested) Classes</h3><p>In Java, classes can be nested inside other classes. They are called <em>inner classes</em> and by convention,
|
|
separated from their outer class by a dollar sign (<code>$</code>):</p><pre><code class="clojure">(import java.util.Map$Entry)
|
|
|
|
Map$Entry ; ⇒ java.util.Map$Entry
|
|
|
|
;; this example assumes RabbitMQ Java client is on classpath
|
|
(import com.rabbitmq.client.AMQP$BasicProperties)
|
|
|
|
AMQP$BasicProperties ; ⇒ com.rabbitmq.client.AMQP$BasicProperties
|
|
</code></pre><p>Note that if you need to use both a class and one or more of its inner classes, they all need to be imported separately.
|
|
As far as JVM is concerned, they are all separate classes, there is no "imports hierarchy".</p><h2 id="how-to-instantiate-java-classes">How to Instantiate Java Classes</h2><p>Java classes are instantiated using the <code>new</code> special form:</p><pre><code class="clojure">(new java.util.Date) ; ⇒ #inst "2012-10-09T21:23:57.278-00:00"
|
|
</code></pre><p>However, the Clojure reader provides a bit of syntactic sugar and you are much more likely
|
|
to see this:</p><pre><code class="clojure">(java.util.Date.) ; ⇒ #inst "2012-10-09T21:24:43.878-00:00"
|
|
</code></pre><p>It is possible to use fully qualified names (e.g. <code>java.util.Date</code>) or short names with imports:</p><pre><code class="clojure">(import java.util.Date)
|
|
|
|
(Date.) ; ⇒ #inst "2012-10-09T21:24:27.229-00:00"
|
|
</code></pre><p>An example with constructor arguments:</p><pre><code class="clojure">(java.net.URI. "http://clojure.org") ; ⇒ #<URI http://clojure.org>
|
|
</code></pre><h2 id="how-to-invoke-java-methods">How to Invoke Java Methods</h2><h3 id="instance-methods">Instance Methods</h3><p>Instance methods are invoked using the <code>.</code> special form:</p><pre><code class="clojure">(let [d (java.util.Date.)]
|
|
(. d getTime)) ; ⇒ 1349819873183
|
|
</code></pre><p>Just like with object instantiation, it is much more common to see an alternative version:</p><pre><code class="clojure">(let [d (java.util.Date.)]
|
|
(.getTime d)) ; ⇒ 1349819873183
|
|
</code></pre><h3 id="static-methods">Static Methods</h3><p>Static methods can be invoked with the same <code>.</code> special form:</p><pre><code class="clojure">(. Math floor 5.677) ; ⇒ 5.0
|
|
</code></pre><p>or (typically) to sugared version, <code>ClassName/methodName</code>:</p><pre><code class="clojure">(Math/floor 5.677) ; ⇒ 5.0
|
|
|
|
(Boolean/valueOf "false") ; ⇒ false
|
|
(Boolean/valueOf "true") ; ⇒ true
|
|
</code></pre><h3 id="chained-calls-with-the-double-dot-form">Chained Calls With The Double Dot Form</h3><p>It is possible to chain method calls using the <code>..</code> special form:</p><pre><code class="clojure">(.. (java.util.Date.) getTime toString) ; ⇒ "1349821993809"
|
|
</code></pre><h3 id="multiple-calls-on-the-same-object">Multiple Calls On the Same Object</h3><p>If you need to call a bunch of methods on a mutable object, you
|
|
can use the <code>doto</code> macro:</p><pre><code class="clojure">(doto (java.util.Stack.)
|
|
(.push 42)
|
|
(.push 13)
|
|
(.push 7)) ; ⇒ #<Stack [42, 13, 7]>
|
|
|
|
(let [pt (Point. 0 0)]
|
|
(doto pt
|
|
(.move 10 0))) ; ⇒ #<Point java.awt.Point[x=10, y=0]
|
|
|
|
(let [pt (Point. 0 0)]
|
|
(doto pt
|
|
(.move 10 0)
|
|
(.translate 0 10))) ; ⇒ #<Point java.awt.point[x=10,y=10]
|
|
</code></pre><p>The <code>doto</code> macro returns its first argument as a result.</p><h2 id="how-to-access-java-fields">How to Access Java Fields</h2><p>Public mutable fields are not common in Java libraries but sometimes you need to access them.
|
|
It's done with the same dot special form:</p><pre><code class="clojure">(import java.awt.Point)
|
|
|
|
(let [pt (Point. 0 10)]
|
|
(. pt x)) ; ⇒ 0
|
|
|
|
(let [pt (Point. 0 10)]
|
|
(. pt y)) ; ⇒ 10
|
|
</code></pre><p>and just like with instance methods, it is much more common to see the following version:</p><pre><code class="clojure">(import java.awt.Point)
|
|
|
|
(let [pt (Point. 0 10)]
|
|
(.x pt)) ; ⇒ 0
|
|
|
|
(let [pt (Point. 0 10)]
|
|
(.y pt)) ; ⇒ 10
|
|
</code></pre><h2 id="how-to-set-java-fields">How to Set Java Fields</h2><p>To set a public mutable field, use <code>clojure.core/set!</code> that takes a field in the dot notation
|
|
demonstrated earlier and a new value:</p><pre><code class="clojure">(import java.awt.Point)
|
|
|
|
(let [pt (Point. 0 10)]
|
|
(set! (.y pt) 100)
|
|
(.y pt)) ; ⇒ 100
|
|
</code></pre><p>Fortunately, mutable public fields are rare to meet in the JVM ecosystem so you won't need
|
|
to do this often.</p><h2 id="how-to-work-with-enums">How To Work With Enums</h2><p><a href="http://docs.oracle.com/javase/tutorial/java/javaOO/enum.html">Enums (enumeration) type</a> values are accessed
|
|
the same way as fields, except on enum classes and not objects:</p><pre><code class="clojure">java.util.concurrent.TimeUnit/MILLISECONDS ; ⇒ #< MILLISECONDS>
|
|
</code></pre><h2 id="determining-classes-of-java-objects">Determining Classes of Java Objects</h2><p>To get class of a particular value, pass it to <code>clojure.core/class</code>:</p><pre><code class="clojure">(class 1) ; ⇒ java.lang.Long
|
|
(class 1.0) ; ⇒ java.lang.Double
|
|
(class "docs") ; ⇒ java.lang.String
|
|
(class (java.net.URI. "https://github.com")) ; ⇒ java.net.URI
|
|
</code></pre><p>As this example demonstrates, Clojure strings are JVM strings, integer literals are compiled
|
|
as longs and floating point literals are compiled as doubles.</p><p>You can also use <code>clojure.core/type</code> to return either the class of the
|
|
Java object, or the <code>:type</code> metadata if it exists:</p><pre><code class="clojure">(def foo (with-meta [1 2 3] {:type :bar}))
|
|
(type foo)
|
|
;; ⇒ :bar
|
|
(type [1 2 3])
|
|
;; ⇒ clojure.lang.PersistentVector
|
|
</code></pre><h2 id="how-to-get-a-java-class-reference-by-name">How To Get a Java Class Reference By Name</h2><p>To obtain a class reference by its string name (fully qualified), use <code>Class/forName</code> via Java interop:</p><pre><code class="clojure">(Class/forName "java.util.Date") ; ⇒ java.util.Date
|
|
</code></pre><h3 id="array-types-primitives">Array Types, Primitives</h3><p>JVM has what is called <strong>primitive types</strong> (numerics, chars, booleans) that are not "real" objects.
|
|
In addition, array types have pretty obscure internal names. If you need to obtain a reference to
|
|
an array of longs, for example, pass <code>"[[J"</code> to <code>Class/forName</code>. Below is the full table:</p><table class="table-striped table-bordered table"><thead><tr><th>Internal JVM class name</th><th>Array of ? (type)</th></tr></thead><tbody><tr><td><pre>"[[S"</pre></td><td>short</td></tr><tr><td><pre>"[[I"</pre></td><td>integer</td></tr><tr><td><pre>"[[J"</pre></td><td>long</td></tr><tr><td><pre>"[[F"</pre></td><td>float</td></tr><tr><td><pre>"[[D"</pre></td><td>double</td></tr><tr><td><pre>"[[B"</pre></td><td>byte</td></tr><tr><td><pre>"[[C"</pre></td><td>char</td></tr><tr><td><pre>"[[Z"</pre></td><td>boolean</td></tr></tbody></table><p>If this does not make much sense, don't worry. Just remember to come
|
|
back to this guide when you need to extend a protocol for an array of
|
|
primitives.</p><h2 id="implementing-java-interfaces-with-reify">Implementing Java Interfaces With reify</h2><p>It is possible to implement Java interfaces in Clojure. It is
|
|
typically needed to interact with Java libraries that take arguments
|
|
implementing a particular interface.</p><p>Interfaces are implemented using the <code>reify</code> special form.</p><p>Given the following Java interface:</p><pre><code class="java">public
|
|
interface FilenameFilter {
|
|
/**
|
|
* Tests if a specified file should be included in a file list.
|
|
*
|
|
* @param dir the directory in which the file was found.
|
|
* @param name the name of the file.
|
|
* @return <code>true</code> if and only if the name should be
|
|
* included in the file list; <code>false</code> otherwise.
|
|
*/
|
|
boolean accept(File dir, String name);
|
|
}
|
|
</code></pre><p>here is how to implement it in Clojure:</p><pre><code class="clojure">;; a FileFilter implementation that accepts everything
|
|
(reify java.io.FilenameFilter
|
|
(accept [this dir name]
|
|
true))
|
|
</code></pre><p><code>reify</code> takes an interface (fully-qualified name or short name) and one or more
|
|
method implementations that mimic function definitions without the <code>defn</code> and with
|
|
<em>this</em> (as in Java, JavaScript or <em>self</em> in Ruby, Python) reference being the first argument:</p><pre><code class="clojure">(accept [this dir name]
|
|
true)
|
|
</code></pre><p>With <code>reify</code>, generally there is no need to add type hints on arguments: Clojure
|
|
compiler typically will detect the best matching method (by name and number of arguments).</p><p><code>reify</code> returns a <em>Java class instance</em>. Clojure compiler will generate a class that implements
|
|
the interface and instantiate it. To demonstrate that reified objects indeed implement
|
|
the interface:</p><pre><code class="clojure">(let [ff (reify java.io.FilenameFilter
|
|
(accept [this dir name]
|
|
true))]
|
|
(instance? java.io.FileFilter ff)) ; ⇒ true
|
|
</code></pre><p><code>reify</code> can be used to implement multiple interfaces at once:</p><pre><code class="clojure">(let [ff (reify java.io.FilenameFilter
|
|
(accept [this dir name]
|
|
true)
|
|
|
|
java.io.FileFilter
|
|
(accept [this dir]
|
|
true))]
|
|
(instance? java.io.FileFilter ff)) ; ⇒ true
|
|
</code></pre><h3 id="reify-parameter-destructuring-and-varargs">reify, Parameter Destructuring and Varargs</h3><p><code>reify</code> does not support destructuring or variadic number of arguments in method signatures.
|
|
For example, the following will not work and won't even compile in Clojure 1.5:</p><pre><code class="clojure">(reify com.megacorp.api.AnInterface
|
|
(aMethod [a [b c]]
|
|
(comment ...))
|
|
(anotherMethod [a & rest]
|
|
(comment ...)))
|
|
</code></pre><h3 id="example-1">Example 1</h3><p>The following example demonstrates how instances created with <code>reify</code> are passed around
|
|
as regular Java objects:</p><pre><code class="clojure">(import java.io.File)
|
|
|
|
;; a file filter implementation that keeps only .clj files
|
|
(let [ff (reify java.io.FilenameFilter
|
|
(accept [this dir name]
|
|
(.endsWith name ".clj")))
|
|
dir (File. "/Users/antares/Development/ClojureWerkz/neocons.git/")]
|
|
(into [] (.listFiles dir ff)))
|
|
;; ⇒ [#<File /Users/antares/Development/ClojureWerkz/neocons.git/project.clj>]
|
|
</code></pre><p><code>reify</code> forms a closure: it will capture locals in its scope. This can be used to make implemented
|
|
methods delegate to Clojure functions. The same example, rewritten with delegation:</p><pre><code class="clojure">user> (import java.io.File)
|
|
|
|
;; a file filter implementation that keeps only .clj files
|
|
(let [f (fn [^File dir ^String name]
|
|
(.endsWith name ".clj"))
|
|
ff (reify java.io.FilenameFilter
|
|
(accept [this dir name]
|
|
(f dir name)))
|
|
dir (File. "/Users/antares/Development/ClojureWerkz/neocons.git/")]
|
|
(into [] (.listFiles dir ff)))
|
|
;; ⇒ [#<File /Users/antares/Development/ClojureWerkz/neocons.git/project.clj>]
|
|
</code></pre><p>Note that unlike in the "inline" implementation, Clojure compiler cannot infer types of
|
|
<code>dir</code> and <code>name</code> parameters in the function that does the filtering, so we added type hints
|
|
to avoid reflective calls. When methods are implemented "inline", types can be inferred from
|
|
method signatures in the interface.</p><h2 id="extending-java-classes-with-proxy">Extending Java Classes With proxy</h2><p><code>proxy</code> is one of two ways to generate instances of anonymous classes in Clojure.
|
|
<code>proxy</code> takes two vectors: one listing its superclass and (optional) interfaces, another constructor signatures, as well as
|
|
method implementations. Method implementations are basically identical to <code>reify</code> except that the <code>this</code> argument is
|
|
not necessary.</p><p>A very minimalistic example, we instantiate an anonymous class that extends <code>java.lang.Object</code>, implements no
|
|
interfaces, has no explictly defined constructors and overrides <code>#toString</code>:</p><pre><code class="clojure">(proxy [Object] []
|
|
(toString []
|
|
"I am an instance of an anonymous class generated via proxy"))
|
|
;; ⇒ #<Object$0 I am an instance of an anonymous class generated via proxy>
|
|
</code></pre><p>Clojure compiler will generate an anonymous class for this <code>proxy</code> and at runtime, the cost of
|
|
a <code>proxy</code> call is the cost of instantiating this class (the class is not generated anew on every single call).</p><p>A slightly more complex example where the generated class also implements <code>java.lang.Runnable</code> (runnable objects
|
|
are commonly used with threads and <code>java.util.concurrent</code> classes) which defines one method, <code>#run</code>:</p><pre><code class="clojure">;; extends java.lang.Object, implements java.lang.Runnable
|
|
(let [runnable (proxy [Object Runnable] []
|
|
(toString []
|
|
"I am an instance of an anonymous class generated via proxy")
|
|
(run []
|
|
(println "Run, proxy, run")))]
|
|
(.run runnable)) ; ⇒ nil
|
|
;; outputs "Run, proxy, run"
|
|
</code></pre><p><code>proxy</code> forms a closure: it will capture locals in its scope. This is very often used to create an instance
|
|
that delegates to a Clojure function:</p><pre><code class="clojure">(let [f (fn [] (println "Executed from a function"))
|
|
obj (proxy [Object Runnable] []
|
|
(run []
|
|
(f)))]
|
|
(.run obj)) ; ⇒ nil
|
|
;; outputs "Executed from a function"
|
|
</code></pre><p>TBD: more realistic examples | <a href="https://github.com/clojure-doc/clojure-doc.github.io#how-to-contribute">How to Contribute</a></p><h2 id="clojure-functions-implement-runnable-and-callable">Clojure Functions Implement Runnable and Callable</h2><p>Note that Clojure functions implement <code>java.lang.Runnable</code> and
|
|
<code>java.util.concurrent.Callable</code> directly so you can pass functions to
|
|
methods found in various classes from the <code>java.util.concurrent</code> package.</p><p>For example, to run a function in a new thread:</p><pre><code class="clojure">(let [t (Thread. (fn []
|
|
(println "I am running in a separate thread")))]
|
|
(.start t))
|
|
</code></pre><p>Or submit a function for execution to a thread pool (in JDK terms: an execution service):</p><pre><code class="clojure">(import '[java.util.concurrent Executors ExecutorService Callable])
|
|
|
|
(let [^ExecutorService pool (Executors/newFixedThreadPool 16)
|
|
^Callable clbl (cast Callable (fn []
|
|
(reduce + (range 0 10000))))
|
|
task (.submit pool clbl)]
|
|
(.get task))
|
|
;; ⇒ 49995000
|
|
</code></pre><p>Note that without the cast, Clojure compiler would not be able to determine
|
|
which exact version of the method we intend to invoke, because <code>java.util.concurrent.ExecutionService/submit</code>
|
|
has two versions, one for <code>Runnable</code> and one for <code>Callable</code>. They work very much the same but return
|
|
slightly different results (<code>Callable</code> produces a value while <code>Runnable</code> always returns nil when
|
|
executed).</p><p>The exception we would get without the cast is</p><pre><code>CompilerException java.lang.IllegalArgumentException: More than one matching method found: submit, compiling:(NO_SOURCE_PATH:2)
|
|
</code></pre><h2 id="gen-class-and-how-to-implement-java-classes-in-clojure">gen-class and How to Implement Java Classes in Clojure</h2><h3 id="overview-1">Overview</h3><p><code>gen-class</code> is a Clojure feature for implementing Java classes in Clojure. It is relatively
|
|
rarely used compared to <code>proxy</code> and <code>reify</code> but is needed to implement executable classes
|
|
(that <code>java</code> runner and IDEs can as program entry points).</p><p>Unlike <code>proxy</code> and <code>reify</code>, <code>gen-class</code> defines named classes. They can be passed to Java
|
|
APIs that expect class references. Classes defined with <code>gen-class</code> can extend
|
|
base classes, implement any number of Java interfaces, define any number of constructors
|
|
and define both instance and static methods.</p><h3 id="aot">AOT</h3><p><code>gen-class</code> requires <em>ahead-of-time</em> (AOT) compilation. It means that
|
|
before using the classes defined with <code>gen-class</code>, the Clojure
|
|
compiler needs to produce <code>.class</code> files from <code>gen-class</code> definitions.</p><h3 id="class-definition-with-clojurecoregen-class">Class Definition With clojure.core/gen-class</h3><p><code>clojure.core/gen-class</code> is a macro that uses a DSL for defining class
|
|
methods, base class, implemented interfaces and so on.</p><p>It takes a number of options:</p><ul><li><code>:name</code> (a symbol): defines generated class name</li><li><code>:extends</code> (a symbol): name of the base class</li><li><code>:implements</code> (a collection): interfaces the class implements</li><li><code>:constructors</code> (a map): constructor signatures</li><li><code>:methods</code> (a collection): lists methods that will be implemented</li><li><code>:init</code> (symbol): defines a function that will be invoked with constructor arguments</li><li><code>:post-init</code> (symbol): defines a function that will be called with a constructed instance as its first argument</li><li><code>:state</code> (symbol): if supplied, a public final instance field with the given name will be created. Only makes sense when
|
|
used with <code>:init</code>. State field value should be an atom or other ref type to allow state mutation.</li><li><code>:prefix</code> (string, default: <code>"-"</code>): methods will call functions named as <code>(str prefix method-name)</code>, e.g. <code>-getName</code> for <code>getName</code>.</li><li><code>:main</code> (boolean): if <code>true</code>, a public static main method will be generated for the class. It will delegate
|
|
to a function named main with the prefix (<code>(str prefix "main")</code>), <code>-main</code> by default</li><li><code>:exposes</code>: TBD</li><li><code>:exposes-methods</code>: TBD</li><li><code>:factory</code>: TBD</li><li><code>:load-impl-ns</code>: TBD</li><li><code>:impl-ns</code>: TBD</li></ul><h4 id="the-name-option">The :name Option</h4><p>TBD</p><h4 id="the-extends-option">The :extends Option</h4><p>TBD</p><h4 id="the-implements-option">The :implements Option</h4><p>TBD</p><h4 id="the-constructors-option">The :constructors Option</h4><p>TBD</p><h4 id="the-methods-option">The :methods Option</h4><p>TBD</p><h4 id="the-init-option">The :init Option</h4><p>TBD</p><h4 id="the-post-init-option">The :post-init Option</h4><p>TBD</p><h4 id="the-state-option">The :state Option</h4><p>TBD</p><h4 id="the-prefix-option">The :prefix Option</h4><p>TBD</p><h4 id="the-main-option">The :main Option</h4><p>TBD</p><h4 id="the-exposes-option">The :exposes Option</h4><p>TBD</p><h4 id="the-exposes-methods-option">The :exposes-methods Option</h4><p>TBD</p><h4 id="the-factory-option">The :factory Option</h4><p>TBD</p><h4 id="the-load-impl-ns-option">The :load-impl-ns Option</h4><p>TBD</p><h4 id="the-impl-ns-option">The :impl-ns Option</h4><p>TBD</p><h3 id="gen-class-in-the-ns-macro">gen-class In The ns Macro</h3><p><code>gen-class</code> can be used with existing namespaces by adding <code>(:gen-class)</code> to the
|
|
<code>ns</code> macro. Here is a "hello, world" example command line app that uses <code>gen-class</code>
|
|
to generate a class that JVM launcher (<code>java</code>) can run:</p><pre><code class="clojure">(ns genclassy.core
|
|
(:gen-class))
|
|
|
|
(defn -main
|
|
[& args]
|
|
(println "Hello, World!"))
|
|
</code></pre><p>This will use the name of the namespace for class name and use the namespace for method
|
|
implementation (see the <code>:impl-ns</code> option above).</p><h3 id="examples">Examples</h3><p>A medium size example taken from an open source library:</p><pre><code class="clojure">(ns clojurewerkz.quartzite.listeners.amqp.PublishingSchedulerListener
|
|
(:gen-class :implements [org.quartz.SchedulerListener]
|
|
:init init
|
|
:state state
|
|
:constructors {[com.rabbitmq.client.Channel String String] []})
|
|
(:require [langohr.basic :as lhb]
|
|
[clojure.data.json :as json])
|
|
(:use [clojurewerkz.quartzite.conversion])
|
|
(:import [org.quartz SchedulerListener SchedulerException Trigger TriggerKey JobDetail JobKey]
|
|
[com.rabbitmq.client Channel]
|
|
[java.util Date]
|
|
[clojurewerkz.quartzite.listeners.amqp PublishingSchedulerListener]))
|
|
|
|
|
|
|
|
(defn publish
|
|
[^PublishingSchedulerListener this payload ^String type]
|
|
(let [{ :keys [channel exchange routing-key] } @(.state this)
|
|
payload (json/json-str payload)]
|
|
(lhb/publish channel exchange routing-key payload :type type)))
|
|
|
|
|
|
(defn -init
|
|
[^Channel ch ^String exchange ^String routing-key]
|
|
[[] (atom { :channel ch :exchange exchange :routing-key routing-key })])
|
|
|
|
|
|
(defmacro payloadless-publisher
|
|
[method-name message-type]
|
|
`(defn ~method-name
|
|
[this#]
|
|
(publish this# (json/json-str {}) ~message-type)))
|
|
|
|
(payloadless-publisher -schedulerStarted "quartz.scheduler.started")
|
|
(payloadless-publisher -schedulerInStandbyMode "quartz.scheduler.standby")
|
|
(payloadless-publisher -schedulingDataCleared "quartz.scheduler.cleared")
|
|
(payloadless-publisher -schedulerShuttingDown "quartz.scheduler.shutdown")
|
|
|
|
|
|
(defn -schedulerError
|
|
[this ^String msg ^SchedulerException cause]
|
|
(publish this (json/json-str { :message msg :cause (str cause) }) "quartz.scheduler.error"))
|
|
|
|
|
|
(defn -jobScheduled
|
|
[this ^Trigger trigger]
|
|
(publish this (json/json-str { :group (-> trigger .getKey .getGroup) :key (-> trigger .getKey .getName) :description (.getDescription trigger) }) "quartz.scheduler.job-scheduled"))
|
|
|
|
(defn -jobUnscheduled
|
|
[this ^TriggerKey key]
|
|
(publish this (json/json-str { :group (.getGroup key) :key (.getName key) }) "quartz.scheduler.job-unscheduled"))
|
|
|
|
(defn -triggerFinalized
|
|
[this ^Trigger trigger]
|
|
(publish this (json/json-str { :group (-> trigger .getKey .getGroup) :key (-> trigger .getKey .getName) :description (.getDescription trigger) }) "quartz.scheduler.trigger-finalized"))
|
|
|
|
(defn -triggerPaused
|
|
[this ^TriggerKey key]
|
|
(publish this (json/json-str { :group (.getGroup key) :key (.getName key) }) "quartz.scheduler.trigger-paused"))
|
|
|
|
(defn -triggersPaused
|
|
[this ^String trigger-group]
|
|
(publish this (json/json-str { :group trigger-group }) "quartz.scheduler.triggers-paused"))
|
|
|
|
(defn -triggerResumed
|
|
[this ^TriggerKey key]
|
|
(publish this (json/json-str { :group (.getGroup key) :key (.getName key) }) "quartz.scheduler.trigger-resumed"))
|
|
|
|
(defn -triggersResumed
|
|
[this ^String trigger-group]
|
|
(publish this (json/json-str { :group trigger-group }) "quartz.scheduler.triggers-resumed"))
|
|
|
|
|
|
|
|
(defn -jobAdded
|
|
[this ^JobDetail detail]
|
|
(publish this (json/json-str { :job-detail (from-job-data (.getJobDataMap detail)) :description (.getDescription detail) }) "quartz.scheduler.job-added"))
|
|
|
|
(defn -jobDeleted
|
|
[this ^JobKey key]
|
|
(publish this (json/json-str { :group (.getGroup key) :key (.getName key) }) "quartz.scheduler.job-deleted"))
|
|
|
|
(defn -jobPaused
|
|
[this ^JobKey key]
|
|
(publish this (json/json-str { :group (.getGroup key) :key (.getName key) }) "quartz.scheduler.job-paused"))
|
|
|
|
(defn -jobsPaused
|
|
[this ^String job-group]
|
|
(publish this (json/json-str { :group job-group }) "quartz.scheduler.jobs-paused"))
|
|
|
|
(defn -jobResumed
|
|
[this ^JobKey key]
|
|
(publish this (json/json-str { :group (.getGroup key) :key (.getName key) }) "quartz.scheduler.job-resumed"))
|
|
|
|
(defn -jobsResumed
|
|
[this ^String job-group]
|
|
(publish this (json/json-str { :group job-group }) "quartz.scheduler.jobs-resumed"))
|
|
</code></pre><h3 id="inspecting-class-signatures">Inspecting Class Signatures</h3><p>When using <code>gen-class</code> for interoperability purposes, sometimes it is necessary to inspect the API
|
|
of the class generated by <code>gen-class</code>.</p><p>It can be inspected
|
|
using <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javap.html">javap</a>. Given the
|
|
following Clojure namespace:</p><pre><code class="clojure">(ns genclassy.core
|
|
(:gen-class))
|
|
|
|
(defn -main
|
|
[& args]
|
|
(println "Hello, World!"))
|
|
</code></pre><p>We can inspect the produced class like so:</p><pre><code># from target/classes, default .class files location used by Leiningen
|
|
javap genclassy.core
|
|
</code></pre><p>will output</p><pre><code class="java">public class genclassy.core {
|
|
public static {};
|
|
public genclassy.core();
|
|
public java.lang.Object clone();
|
|
public int hashCode();
|
|
public java.lang.String toString();
|
|
public boolean equals(java.lang.Object);
|
|
public static void main(java.lang.String[]);
|
|
}
|
|
</code></pre><h2 id="how-to-extend-protocols-to-java-classes">How To Extend Protocols to Java Classes</h2><p>Clojure protocols can be extended to any java class (including
|
|
Clojure's internal types) very easily using <code>extend</code>:</p><p>Using the example of a json library, we can define our goal as getting
|
|
to the point where the following works:</p><pre><code class="clojure">(json-encode (java.util.UUID/randomUUID))
|
|
</code></pre><p>First, let's start with the protocol for json encoding an object:</p><pre><code class="clojure">(defprotocol JSONable
|
|
(json-encode [obj]))
|
|
</code></pre><p>So, everything that is "JSONable" implements a <code>json-encode</code> method.</p><p>Next, let's define a dummy method to do the "encoding" (in this
|
|
example, it just prints to standard out instead, it doesn't actually
|
|
do any json encoding):</p><pre><code class="clojure">(defn encode-fn
|
|
[x]
|
|
(prn x))
|
|
</code></pre><p>Now, define a method that will encode java objects by calling <code>bean</code>
|
|
on them, then making each value of the bean map a string:</p><pre><code class="clojure">(defn encode-java-thing
|
|
[obj]
|
|
(encode-fn
|
|
(into {}
|
|
(map (fn [m]
|
|
[(key m) (str (val m))])
|
|
(bean obj)))))
|
|
</code></pre><p>Let's try it on an example object, a UUID:</p><pre><code class="clojure">(encode-java-thing (java.util.UUID/randomUUID))
|
|
;; ⇒ {:mostSignificantBits "-6060053801408705927",
|
|
;; :leastSignificantBits "-7978739947533933755",
|
|
;; :class "class java.util.UUID"}
|
|
</code></pre><p>The next step is to extend the protocol to the java type, telling
|
|
clojure which java type to extend, the protocol to implement and the
|
|
method to use for the <code>json-encode</code> method:</p><pre><code class="clojure">(extend java.util.UUID
|
|
JSONable
|
|
{:json-encode encode-java-thing})
|
|
</code></pre><p>Alternatively, you could use the <code>extend-type</code> macro, which actually
|
|
expands into calls to <code>extend</code>:</p><pre><code class="clojure">(extend-type java.util.UUID
|
|
JSONable
|
|
(json-encode [obj] (encode-java-thing obj)))
|
|
</code></pre><p>Now we can use <code>json-encode</code> for the object we've extended:</p><pre><code class="clojure">(json-encode (java.util.UUID/randomUUID))
|
|
;; ⇒ {:mostSignificantBits "3097485598740136901",
|
|
;; :leastSignificantBits "-9000234678473924364",
|
|
;; :class "class java.util.UUID"}
|
|
</code></pre><p>You could also write the function inline in the extend block, for
|
|
example, extending <code>nil</code> to return a warning string:</p><pre><code class="clojure">(extend nil
|
|
JSONable
|
|
{:json-encode (fn [x] "x is nil!")})
|
|
|
|
(json-encode nil)
|
|
;; ⇒ "x is nil!"
|
|
</code></pre><p>The <code>encode-java-thing</code> method can also be reused for other Java types
|
|
we may want to encode:</p><pre><code class="clojure">(extend java.net.URL
|
|
JSONable
|
|
{:json-encode encode-java-thing})
|
|
|
|
(json-encode (java.net.URL. "http://aoeu.com"))
|
|
;; ⇒ {:path "",
|
|
;; :protocol "http",
|
|
;; :authority "aoeu.com",
|
|
;; :host "aoeu.com",
|
|
;; :ref "",
|
|
;; :content "sun.net.www.protocol.http.HttpURLConnection$HttpInputStream@4ecac02f",
|
|
;; :class "class java.net.URL",
|
|
;; :defaultPort "80",
|
|
;; :port "-1",
|
|
;; :query "",
|
|
;; :file "",
|
|
;; :userInfo ""}
|
|
</code></pre><h2 id="using-intrinsic-locks-synchronized-in-clojure">Using Intrinsic Locks ("synchronized") in Clojure</h2><p>Every object on the JVM has an <em>intrinsic lock</em> (also referred to as <em>monitor lock</em>
|
|
or simply <em>monitor</em>). While very rarely necessary, Clojure provides support for
|
|
operations that acquire intrinsic lock of a mutable Java object.</p><p>This is covered in the <a href="../concurrency_and_parallelism/index.html#using_intrinsic_locks_synchronized_in_clojure">Concurrency and Parallelism guide</a>.</p><h2 id="wrapping-up">Wrapping Up</h2><p>TBD: <a href="https://github.com/clojure-doc/clojure-doc.github.io#how-to-contribute">How to Contribute</a></p><h2 id="contributors">Contributors</h2><p>Michael Klishin <a href="mailto:michael@defprotocol.org">michael@defprotocol.org</a> (original author)
|
|
Lee Hinman <a href="mailto:lee@writequit.org">lee@writequit.org</a>
|
|
gsnewmark <a href="mailto:gsnewmark@meta.ua">gsnewmark@meta.ua</a></p>
|
|
|
|
<div id="prev-next">
|
|
|
|
<a href="../laziness/index.html">« Laziness in Clojure</a>
|
|
|
|
|
|
||
|
|
|
|
|
|
<a href="../macros/index.html">Clojure Macros and Metaprogramming »</a>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-md-3">
|
|
<div id="sidebar">
|
|
<h3>Links</h3>
|
|
<ul id="links">
|
|
|
|
<li><a href="../../about/index.html">About</a></li>
|
|
|
|
<li><a href="../../content/index.html">Table of Contents</a></li>
|
|
|
|
<li><a href="../../tutorials/getting_started/index.html">Getting Started with Clojure</a></li>
|
|
|
|
<li><a href="../../tutorials/introduction/index.html">Introduction to Clojure</a></li>
|
|
|
|
<li><a href="../../tutorials/emacs/index.html">Clojure with Emacs</a></li>
|
|
|
|
<li><a href="../../tutorials/vim_fireplace/index.html">Clojure with Vim and fireplace.vim</a></li>
|
|
|
|
<li><a href="../../tutorials/eclipse/index.html">Starting with Eclipse and Counterclockwise For Clojure Development</a></li>
|
|
|
|
<li><a href="../../tutorials/basic_web_development/index.html">Basic Web Development</a></li>
|
|
|
|
<li><a href="../../tutorials/parsing_xml_with_zippers/index.html">Parsing XML in Clojure</a></li>
|
|
|
|
<li><a href="../../tutorials/growing_a_dsl_with_clojure/index.html">Growing a DSL with Clojure</a></li>
|
|
|
|
<li><a href="../core_overview/index.html">Overview of clojure.core, the standard Clojure library</a></li>
|
|
|
|
<li><a href="../namespaces/index.html">Clojure Namespaces and Vars</a></li>
|
|
|
|
<li><a href="../collections_and_sequences/index.html">Collections and Sequences in Clojure</a></li>
|
|
|
|
<li><a href="../functions/index.html">Functions in Clojure</a></li>
|
|
|
|
<li><a href="../laziness/index.html">Laziness in Clojure</a></li>
|
|
|
|
<li><a href="index.html">Clojure interoperability with Java</a></li>
|
|
|
|
<li><a href="../macros/index.html">Clojure Macros and Metaprogramming</a></li>
|
|
|
|
<li><a href="../polymorphism/index.html">Polymorphism in Clojure: Protocols and Multimethods</a></li>
|
|
|
|
<li><a href="../concurrency_and_parallelism/index.html">Concurrency and Parallelism in Clojure</a></li>
|
|
|
|
<li><a href="../glossary/index.html">Clojure Terminology Guide</a></li>
|
|
|
|
<li><a href="../../ecosystem/libraries_directory/index.html">A Directory of Clojure Libraries</a></li>
|
|
|
|
<li><a href="../../ecosystem/libraries_authoring/index.html">Library Development and Distribution</a></li>
|
|
|
|
<li><a href="../../ecosystem/generating_documentation/index.html">Generating Documentation</a></li>
|
|
|
|
<li><a href="../../ecosystem/data_processing/index.html">Data Processing (Help Wanted)</a></li>
|
|
|
|
<li><a href="../../ecosystem/web_development/index.html">Web Development (Overview)</a></li>
|
|
|
|
<li><a href="../../ecosystem/maven/index.html">How to use Maven to build Clojure projects</a></li>
|
|
|
|
<li><a href="../../ecosystem/community/index.html">Clojure Community</a></li>
|
|
|
|
<li><a href="../../ecosystem/user_groups/index.html">Clojure User Groups</a></li>
|
|
|
|
<li><a href="../../ecosystem/running_cljug/index.html">Running a Clojure User Group</a></li>
|
|
|
|
<li><a href="../../ecosystem/books/index.html">Books about Clojure and ClojureScript</a></li>
|
|
|
|
<li><a href="../../cookbooks/data_structures/index.html">Data Structures (Help wanted)</a></li>
|
|
|
|
<li><a href="../../cookbooks/strings/index.html">Strings</a></li>
|
|
|
|
<li><a href="../../cookbooks/math/index.html">Mathematics with Clojure</a></li>
|
|
|
|
<li><a href="../../cookbooks/date_and_time/index.html">Date and Time (Help wanted)</a></li>
|
|
|
|
<li><a href="../../cookbooks/files_and_directories/index.html">Working with Files and Directories in Clojure</a></li>
|
|
|
|
<li><a href="../../cookbooks/middleware/index.html">Middleware in Clojure</a></li>
|
|
|
|
<li><a href="../../ecosystem/java_jdbc/home.html">java.jdbc - Getting Started</a></li>
|
|
|
|
<li><a href="../../ecosystem/java_jdbc/using_sql.html">java.jdbc - Manipulating data with SQL</a></li>
|
|
|
|
<li><a href="../../ecosystem/java_jdbc/using_ddl.html">java.jdbc - Using DDL and Metadata</a></li>
|
|
|
|
<li><a href="../../ecosystem/java_jdbc/reusing_connections.html">java.jdbc - How to reuse database connections</a></li>
|
|
|
|
<li><a href="../../ecosystem/core_typed/home/index.html">core.typed - User Documentation Home</a></li>
|
|
|
|
<li><a href="../../ecosystem/core_typed/user_documentation/index.html">core.typed - User Documentation</a></li>
|
|
|
|
<li><a href="../../ecosystem/core_typed/rationale/index.html">core.typed - Rationale</a></li>
|
|
|
|
<li><a href="../../ecosystem/core_typed/quick_guide.html">core.typed - Quick Guide</a></li>
|
|
|
|
<li><a href="../../ecosystem/core_typed/start/introduction_and_motivation/index.html">core.typed - Getting Started: Introduction and Motivation</a></li>
|
|
|
|
<li><a href="../../ecosystem/core_typed/types/index.html">core.typed - Types</a></li>
|
|
|
|
<li><a href="../../ecosystem/core_typed/start/annotations/index.html">core.typed - Annotations</a></li>
|
|
|
|
<li><a href="../../ecosystem/core_typed/poly_fn/index.html">core.typed - Polymorphic Functions</a></li>
|
|
|
|
<li><a href="../../ecosystem/core_typed/filters/index.html">core.typed - Filters</a></li>
|
|
|
|
<li><a href="../../ecosystem/core_typed/mm_protocol_datatypes/index.html">core.typed - Protocols</a></li>
|
|
|
|
<li><a href="../../ecosystem/core_typed/loops/index.html">core.typed - Looping constructs</a></li>
|
|
|
|
<li><a href="../../ecosystem/core_typed/function_types/index.html">core.typed - Functions</a></li>
|
|
|
|
<li><a href="../../ecosystem/core_typed/limitations/index.html">core.typed - Limitations</a></li>
|
|
|
|
</ul>
|
|
|
|
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<footer>Copyright © 2021 Multiple Authors
|
|
<p style="text-align: center;">Powered by <a href="http://cryogenweb.org">Cryogen</a></p></footer>
|
|
</div>
|
|
<script src="https://code.jquery.com/jquery-1.11.0.min.js"></script>
|
|
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/js/bootstrap.min.js"></script>
|
|
<script src="../../../js/highlight.pack.js" type="application/javascript"></script>
|
|
<script>hljs.initHighlightingOnLoad();</script>
|
|
|
|
|
|
</body>
|
|
</html>
|