Clojure is built on Java not only because it is a portable, feature-rich platform, but because thousands of libraries, both open-source and commercial, are written in Java. Clojure can leverage all this existing code to get past the "library problem" that plagues most new programming languages.
Clojure does not come packaged with libraries to handle common tasks like file I/O, networking, and database connections. While the number of extant Clojure libraries is growing rapidly, it is still quite small. Fortunately, for any task you might have in mind, there almost certainly exists a Java library to help you with it. The JVM itself comes with over 4000 classes covering everything from networking to GUIs. Clojure is designed to make working with Java libraries as seamless as possible.
Clojure uses just three special forms to handle all interactions with Java classes. The new
special form creates an instance of a class.
(newclassname
&constructor-arguments
)
new
takes the name of a class (a symbol, which will not be evaluated) as its first argument, followed by any arguments for the class's constructor function. The following are some examples:
user> (new String) "" user> (new java.util.Date) #<Date Thu Oct 29 17:04:19 EDT 2009> user> (new java.util.Date 55 10 12) #<Date Sat Nov 12 00:00:00 EST 1955>
The . (dot) special form calls Java methods or fields.
(.target name
&arguments
)
The target
argument may be either a class name or an arbitrary expression. If the target
argument is a class name, then the name
should be a symbol (which is not evaluated) naming a public static method or field of that class. For example, the following code:
user=> (. Integer valueOf "42") 42
user=> (. Integer MAX_VALUE) 2147483647
These are equivalent to this Java code:
Integer.valueOf(42); Integer.MAX_VALUE;
If target
is not a class name, then it will be evaluated normally, and name
should be the name of a public instance method or field of the resulting object. Here are some examples:
user=> (def s "Hello, World!") #'user/s user=> (. s substring 0 5) "Hello"
The second expression is equivalent to the Java code:
s.substring(0, 5);
To set the value of public fields, you can use the set!
special form like this:
(set! (.target name
)value
)
As shown, target
is an object or symbol naming a class, name
is a symbol naming a public field of that class or object, and value
is any expression. This is equivalent to the Java code:
target
.name
=value
;
The new
, . (dot), and set!
special forms are just that, special. They do not obey the same rules for evaluation as normal Clojure functions and macros. In particular, the name
argument is never evaluated, so it cannot be determined at run-time. You cannot, for example, do the following:
;; bad code! (defn call-method [object method-name] (. object method-name))
That will try to call a method named "method-name" on the object—probably not what you wanted. If you need to determine the name of a method at run-time, there are two ways to achieve it: the Java Reflection API and Clojure's eval
function. The former is preferred, but consult the Reflection API documentation for details.[6]
While the new
and . (dot) special forms are sufficient for Java interop, some additional syntax helps Java fit better with Clojure's Lisp-based syntax.
First, Java method calls can be made to look more like Clojure function calls by putting the method name at the head of a list, prefixed by a period:
(.method object arguments
)
The .method
form will be processed by the Clojure compiler as if it were a macro that expands to:
(. object method arguments)
By "as if it were a macro," I mean that this feature is a purely syntactic abstraction or "syntactic sugar." It does not magically transform Java methods into first-class functions.[7] For example, you cannot use .method
as a function argument to map
. Instead, you must wrap the method in a Clojure function:
user=> (map #(.toUpperCase %) ["one" "two" "three"]) ("ONE" "TWO" "THREE")
The Clojure macro memfn
was created for this purpose before the anonymous function syntax #()
existed. memfn
takes a symbol and expands to an anonymous function that calls the method named by that symbol. The anonymous function in the preceding example could have been written (memfn toUpperCase)
, but the #()
form is shorter and preferred.
New instances of Java classes can be constructed by placing the class name at the head of a list, followed by a period:
user=> (java.util.Date. 110 3 12) #<Date Mon Apr 12 00:00:00 EDT 2010> user=> (StringBuilder. "Hello") #<StringBuilder Hello>
You can call static methods with the syntax (ClassName/method arguments)
and retrieve the value of a static field with ClassName/field
. For example, the following code:
user=> (Integer/parseInt "101") 101 user=> Integer/MIN_VALUE -2147483648
Since these "syntactic sugar" expansions happen in the same compilation phase as macro-expansion, macros that do complex code-generation may need to avoid them and use the new
and . (dot) special forms directly. In all other cases, the "syntactic sugar" forms are preferred.
One of Java's strengths as a platform is the provision of generic interfaces for common datatypes such as lists and sets. Clojure's data structures implement these interfaces where appropriate, so if you need to call a Java method that expects, for example, a java.util.List, you can pass it a Clojure data structure without any conversion. Table 10-1 shows which interfaces are implemented by each of the built-in Clojure types.
Be aware that Clojure's collection types (list, vector, map, and set) are still immutable, so they only implement the read-only portions of the java.util.Collection interfaces. Calling a mutating method (such as List.add
or Map.put
) on an immutable object will throw an UnsupportedOperationException
.
What about Java generics like List<String>
or Map<Integer,Object>
? Fortunately, Clojure code never needs to worry about generics due to the way they are implemented in the JVM. Generic types are ignored in Java bytecode; they exist only as hints to the Java language compiler.[8] The Java type List<String>
, when compiled, is just plain List
. What this means for Clojure is that you can call a Java method expecting a generic type (e.g., List<String>
) with an instance of the base collection type (List
). As long as the collection contains objects of the correct type (String
), it just works.
Java arrays lack the concurrency safety of Clojure's collection types; they are mutable and non-thread-safe. However, some Java APIs use arrays for function arguments or return values, so it is necessary to work with them.
In addition, some algorithms can be implemented more efficiently with primitive arrays, especially algorithms that deal with very large collections of primitive types. Using primitive arrays for performance will be discussed in Chapter 14.
You can create a Java array with the make-array function:
(make-arraytype
&dimensions
)
The type
argument must be a class. If you want an array of a Java primitive type, such as int
or double
, you can use the TYPE
field of the corresponding class:
(make-array Double/TYPE 40) ;; creates a double[40] array
If you give only one dimension to make-array
, you get a normal Java array of that length. If you give multiple dimensions, you get a multidimensional array, which is implemented in Java as an array of pointers to other arrays.
In addition to make-array
, there are convenience functions for creating arrays of Java primitive types: int-array
, long-array
, float-array
, and double-array
. Each can be called in several argument forms:
(int-array size)
creates an int[]
array of size
elements.
(int-array size initial-value)
does the same and also sets every element to initial-value
.
(int-array collection)
creates an int[]
array of the same size as collection
, filled with the elements of collection
converted to int
s.
(int-array size collection)
creates an int[]
array of size
elements and fills it with elements from collection
; any unused array elements will be initialized to zero.
The Clojure function to-array
takes any Clojure collection type and returns a Java Object[]
array. If you have a two-dimensional matrix represented as a collection of collections, you can use the to-array-2d
function to produce a 2-dimensional Java array. For example, the following code:
user=> (def matrix [[1 0 0] [0 1 0] [0 0 1]]) #'user/matrix user=> (to-array-2d matrix) #<Object[][] [[Ljava.lang.Object;@540984b>
If you need to convert a collection into an array of a specific type, you can use the into-array
function:
(into-arraycollection
) (into-arraytype collection
)
Called with one argument, a collection, into-array
returns an array of the same type as the first item in the collection. Called with two arguments, the first argument is a class specifying the type of the array. For example, the following code:
user=> (into-array Comparable ["aa" "bb" "cc"]) #<Comparable[] [Ljava.lang.Comparable;@a00185>
You can retrieve a single value from an array (of any dimensionality) with the aget
function:
(agetarray
&indices
)
Setting elements in an array is complicated by the need for special functions for primitive arrays. The aset
function works on any arrays of any Object
type:
(aset array index value) (aset array & indices value)
The setter functions for arrays of primitive types work the same way: aset-boolean
, aset-byte
, aset-char
, aset-short
, aset-int
, aset-long
, aset-float
, and aset-double
. Note that these functions are not very efficient; in fact, they are slower than aset
on type-hinted arrays (see Chapter 14). Use them only when you need to deal with small arrays for Java interop purposes, not for performance.
You can copy an array with the aclone
function, and get its length with the alength
function (although count
also works).
The map
and reduce
functions will work on Java arrays, but they work by converting the arrays to sequences. For slightly greater efficiency, you can iterate over arrays directly using array-specific macros.
(amap a idx ret expr
)
The amap
macro initializes ret
(a symbol) as a clone of the array a
, then evaluates expr
repeatedly with idx
bound to successive indexes of a
. Whatever value is returned by expr
, it will be assigned to the same index of ret
. Finally, amap
returns ret
.
(areduce a idx ret init expr
)
The areduce
macro assigns ret
(a symbol) the value of init
, then evaluates expr
repeatedly with idx
bound to successive indexes of the array a
. Whatever value is returned by expr
becomes the new value of ret
. Finally, areduce
returns the last value of ret
.
Note that both amap
and areduce
are macros implemented in terms of loop/recur, so they take expressions as arguments instead of the functions used by map
and reduce
.
Clojure code can generate real Java classes and methods that can be called like any other Java class. However, if you need to call just a few Clojure functions from your Java code, it may be simpler to user Clojure's Java API, which consists of static methods of the classes clojure.lang.RT, clojure.lang.Compiler, and clojure.lang.Var.
clojure.lang.RT is the Clojure "runtime" class. Remember, Clojure has no interpreter; there cannot be multiple "instances" of Clojure in a single JVM.[9] As a result, most methods of RT are static.
class RT { ... public static void load(Stringname
); public static void loadResourceScript(Stringfilename
); public static void maybeLoadResourceScript(Stringfilename
); ... }
The RT.load
method behaves just like the Clojure load
function described in Chapter 7. The name
argument is the name of a file on the classpath, minus the ".clj" or ".class" extension.
The RT.loadResourceScript
method is similar to load
, except that filename
must include the ".clj" extension. RT.maybeLoadResourceScript
is the same, but will not throw an exception if the file does not exist.
class RT { ... public static Object readString(Stringcode
); ... } class Compiler { ... public static Object eval(Objectobj
); ... }
The RT.readString
method is equivalent to the Clojure read-string
function; it takes a string of Clojure source code and returns the data structure represented by that string. The Compiler.eval
method will evaluate that data structure just like the Clojure eval
function and return the result.
class RT { ... public static Var var(String ns, String name); public static Var var(String ns, String name, Object value); ... }
The RT.var
method returns the Clojure Var with the given namespace and name, creating the namespace and interning the Var (see Chapter 7) as needed. The optional third argument sets the initial value, or root binding, of the Var.
Once you have a Var object, you can retrieve its value with Var.get
, or call it as a function with Var.invoke
:
class Var { ... public Object get(); public Object invoke(Object args...); ... }
Often, Clojure's Java API will not be sufficient for integrating Java code with Clojure code. Many Java libraries require you to implement a particular interface or extend a particular base class. Fortunately, Clojure can create real Java classes, with methods that can be called like any other Java method, without requiring you to write any "wrapper" code in Java.
If you need to implement a Java interface or extend a base class for Java interop purposes, the proxy
macro should be the first place you look. Each time proxy
is evaluated, it creates a new instance of a proxy class, an anonymous class that inherits from the base class and/or interfaces you specify.
(proxy [base-class-and-interfaces
...] [constructor-args
...] (methodName
[params
...]method-body
...) (methodName
...))
The first argument to proxy
is a vector of class and interface names. There may be at most one class (because Java only allows single-class inheritance) and any number of interfaces. If no base class is specified, the proxy class will extend java.lang.Object.
The second argument is a vector of values that should be passed as arguments to the base-class constructor. If the constructor takes no arguments, the vector will be empty, but it must be supplied.
The remaining arguments to proxy are lists of the form (method [args] body)
, where method
is the name of a public or protected member of one of the base classes, args
are the arguments to that method, and body
is the Clojure code that you want to use to implement the method. In effect, you're defining a Clojure function that will be called by the proxy class whenever the named method is invoked.
Multiple-arity methods (methods that take different numbers of arguments) may be implemented like multiple-arity Clojure functions:
(method ([arg] body...) ([arg1 arg2] body...))
All of this sounds complicated, but it's really not. Let's look at a real example. The Java SAX classes implement stream-based XML processing with a "push" interface. To use them, you must provide an instance of a class that implements the org.xml.sax.ContentHandler interface. Clojure's own XML libraries use proxy
for this. Here's a simpler example, a proxy ContentHandler that prints out all the text nodes in the XML document, one per line.
(import '(javax.xml.parsers SAXParserFactory) '(org.xml.sax ContentHandler)
'(org.xml.sax.ext DefaultHandler2) '(java.io File)) (defn proxy-handler [] (proxy [DefaultHandler2] [] ;; DefaultHandler2 constructor takes no args (characters [ch start length] (println (String. ch start length))))) (defn extract-text [filename] (let [parser (.newSAXParser (SAXParserFactory/newInstance))] (.parse parser (File. filename) (proxy-handler))))
The proxy-handler
function returns an instance of a proxy for the class org.xml.sax.ext.DefaultHandler2, which provides no-op implementations of all the org.xml.sax.ContentHandler methods. The proxy class overrides the characters
method, which receives a char
array, and prints the String
form of that array. The extract-text
function creates a new instance of SAXParser using the SAXParserFactory class, then calls the parse
method with the input file and the proxy handler. After loading this code, you can run it like this:
user=> (extract-text "path/to/some/file.xml")
This will print all the text in the XML file. There will be a lot of blank lines, because your implementation does not ignore text elements consisting entirely of whitespace.
Proxy methods can access the object on which they were called as the special local variable this
. For example, to access the value of a public instance field named foo
in the current object, a proxy method could call (.foo this)
.
It is important to remember that proxies are not true subclasses. Although proxies can override protected methods, they cannot access private or protected fields of their "parent" class. They cannot provide their own constructor functions, and they cannot add new methods that are not defined in a parent class or interface. Proxy instances have generated class names like clojure.proxy.org.xml.sax.ext.DefaultHandler2.
Proxy methods do not have direct access to the superclass object as with Java's super
keyword. However, proxies can call superclass methods with the proxy-super
macro:
(proxy-supermethod
&args
)
method
is a symbol (unevaluated) naming a superclass method, args
are the arguments to that method. The corresponding method in the proxied superclass will be invoked on the current object (this
).
While proxy
is usually sufficient for dealing with Java APIs, there are occasions when nothing but a real, concrete Java class will do. You can create such classes in Clojure with the gen-class
macro, which takes a series of key-value pairs as arguments:
(gen-class :name generated-name :extends base-class-name :implements [interfaces ...] :init initialization-function
:constructors {[types ...] [super-types ...], ...} :post-init post-initialization-function :methods [[name [types ...] return-type], ...] :main boolean :factory factory-name :state state-field-name :exposes {field {:get name, :set name}, ...} :exposes-methods {method exposed, ...} :prefix string :impl-ns namespace :load-impl-ns boolean)
No way around it, gen-class
has a ton of parameters. Fortunately, they're all optional except :name
, and you rarely need more than a few of them. Before you get into the options, let's look at how gen-class
works with Java.
When you compile a Java source file with javac
, you get a Java .class file containing Java bytecode. The bytecode defines the fields and methods of that class and their implementations. When you run java
, the Java Virtual Machine loads the .class file and executes the bytecode it contains.[10]
Clojure, by contrast, generates bytecode at run time. You can type an expression at the Clojure REPL, or load a .clj file, and Clojure will compile it on-the-fly into Java bytecode, then pass that bytecode to the Java Virtual Machine for execution. This is fine when all your code is in Clojure, but becomes a problem when you want Java code to be able to call Clojure code, because the executable bytecode for Clojure functions doesn't exist until runtime!
Conceivably, you could write a small "wrapper" class in Java, whose methods invoke Clojure functions through Clojure's API, like this:
import clojure.lang.RT; class MyWrapper { public static Object doStuff() { return RT.var("my-namespace", "do-stuff").invoke(); } }
Then, your Java code could call the method MyWrapper.doStuff()
, which invokes the Clojure function my-namespace/do-stuff
.
Essentially, gen-class
does the same thing, without you having to write any Java code. It generates a Java .class file containing "stub" methods that call Clojure functions.
Because gen-class
needs to generate a .class file, which will presumably be used by other statically-compiled Java classes, it cannot be used at runtime. Instead, it must be invoked in a separate compilation step. Clojure normally compiles code at runtime, so compiling Clojure code before it is run is called ahead-of-time, or AOT, compilation.
Any Clojure namespace can be AOT-compiled. There is usually little reason to do so unless gen-class
is involved. AOT-compiled Clojure code is not faster than dynamically-compiled code, and it still requires the Clojure runtime libraries (clojure.jar). However, AOT-compiled code will start up slightly faster, because the Clojure compiler does not need to compile all the source code when it is loaded, which may be beneficial for large programs.
To compile a namespace, use the compile
function:
(compile name
)
The name
argument is a quoted symbol naming the namespace you want to compile. Clojure will load the source file for that namespace, using the same rules as require
for converting namespace names to file names (see Chapter 7); compile it to Java bytecode and write the bytecode out to .class files in a target directory. One namespace will produce many .class files, one for each function.
The tricky part of AOT-compilation is getting the classpath configured correctly. The target directory where compile
writes .class files is stored in the Var *compile-path*
. When you call compile
, both this directory and the source .clj file must be available on the Java classpath. The default *compile-path*
is "classes", assumed to be a directory within the current working directory. You can change it on the Java command line by setting the Java system property "clojure.compile.path".
Here's an example. Suppose you have a project containing three directories: source code in "source", compiled code in "target", and libraries in "lib". Your Clojure code is in the file "source/com/example/my_library.clj", with the following namespace declaration:
(ns com.example.my-library)
To compile this namespace, you can start Clojure from the root directory of your project like this (all on one line):
java -cp lib/clojure.jar:sources:target -Dclojure.compile.path=target clojure.main
Note that the classpath contains three elements: the Clojure JAR file, the "sources" directory, and the "target" directory. (You would add JAR files for any other libraries your project uses.) In addition, the system property clojure.compile.path is set to "target". The "target" directory must exist! Then, at the Clojure REPL, you can run:
user=> (compile 'com.example.my-library)
This will load the source file from "source/com/example/my_library.clj", compile it, and write a bunch of .class files in the directory "target/com/example/".
Once this is done, you can load and use the namespace com.example.my-library
without the original source files. All you need are the .class files and clojure.jar. Obviously, you shouldn't delete your source files, because you might want to change them and recompile later.
To make it easier to integrate AOT-compilation into build scripts, you can start Java with the class clojure.lang.Compile instead of clojure.main, setting up the classpath and system property as before, passing the namespaces to be compiled as arguments on the command line. In the Apache Ant build system, for example, the XML configuration would contain something like the following snippet:
<java classname="clojure.lang.Compile" classpath="clojure.jar:target:source"> <sysproperty key="clojure.compile.path" value="target"/> <arg value="my.first.namespace"/> <arg value="my.second.namespace"/> </java>
How does gen-class
fit into this? When compile
is compiling a file that calls gen-class
, it generates the additional .class files described by the gen-class
configuration options. At any other time, i.e., when not AOT compiling, gen-class
does nothing.
Now you're ready to tackle the options to gen-class
. You will usually only need the first three options, :name
, :extends
, and :implements
, but we cover them all here. Wherever the arguments call for a class or interface name, that name may be given as either a symbol (which will not be evaluated) or a String, and must be fully-qualified with the Java package name.
The :name
argument is the name of the class to be generated. Remember that this is a Java-style package + class name, so you must use underscores or CamelCase instead of hyphens.
The :extends
argument is the fully-qualified name of a Java class (not an interface) as either a String or a symbol. The generated class will be a subclass of that class.
The :implements
argument is a vector of Java interface names. The generated class will be declared to implement those interfaces and will include stub methods for all the methods defined in those interfaces.
As explained earlier, the class generated by gen-class
will only contain stub methods. The implementations of those methods are normal Clojure functions in a namespace. Each Clojure function will have the same name as its corresponding method, with an added prefix. The prefix defaults to "-", and can be changed with the :prefix
argument to gen-class
. The functions will be called with the object instance as their first argument. For example, if your generated class implements a Java interface with the methods doStuff(int i)
and doMoreStuff(String s)
, your namespace should contain the following function definitions:
(defn -doStuff [this i] ...) (defn -doMoreStuff [this s] ...)
By default, gen-class
uses the current namespace to look up method definitions; this can be changed with the :impl-ns
argument to gen-class
.
You may want to create a class that can be called by Java code but preserves Clojure's notions of immutable state. The :state
argument names a public instance field (of type Object) that will be added to the generated class. Within your methods, you can access the value of this field just like any other Java field. Note that the :state
field is declared final
, so it may not be set outside of the object constructor. Typically, the value of the :state
field will be one of Clojure's mutable reference types (Ref, Agent, or Atom). In this way, you can create stateful Java objects that take advantage of Clojure's transactional semantics.
If your object has :state
, you must provide a way to initialize it. The :init
argument names an "initialization function" that is called before the superclass constructor, with the same arguments as the constructor. The initialization function must return a vector like [[args...] state]
, where state
is the value of the :state
field and args
are the arguments that will be passed back to the superclass constructor.
To do additional computation after the superclass constructor, the :post-init
argument names a function that will be called immediately after the superclass constructor(s), with the newly-constructed object as its argument. The :post-init
function's return value is ignored.
By default, the generated class contains stub methods for all non-private methods of the parent class and interfaces. If you want to add to this set of methods, you can do so with the :methods
option to gen-class
. Its argument is a vector of method signatures, each of the form [name [arg-types...] return-type]
. Those methods are then implemented by Clojure functions with prefixed names just like superclass methods. To create a static method, add :static true
metadata to the signature vector.
For example, suppose you want to add two methods to your class with the following Java signatures:
public int add(int a, int b); public static String getNextID();
You would use gen-class
like this:
(gen-class ... :methods [[add [int int] int] #^{:static true} [getNextID [] String]]) ... (defn -add [this a b] ...) (defn -getNextID [] ...)
Remember that :methods
is only used for adding methods that do not exist in the superclass/superinterfaces. You do not need to redefine the signatures of existing Java methods.
The generated class will automatically have public constructors with type signatures matching those of the superclass constructors. You can add additional constructors with the :constructors
option to gen-class
. The argument to :constructors
is a map of the form {[types...] [super-types...], ...}
. The keys of the map are vectors of argument types for the added constructors, which must map to an existing superclass constructor, identified by a vector of its argument types. For example, if your generated class :extends
a class Foo
with a constructor Foo(int, int)
, and you want to add a constructor that takes a single String
argument, you can do so with the following gen-class form:
(gen-class ... :constructors {[String] [int int]} ...)
You must also supply an :init
function that accepts and returns the appropriate types.
Some Java development styles encourage static factory methods instead of public constructors. You can add static factory methods to your generated class with the :factory
option to gen-class
. Its argument is the name of the generated factory method; this method will be overloaded to accept all the same argument types as the constructors.
Because your method implementations are Clojure functions, not true Java methods, they do not have access to protected fields of the superclass, nor can they call superclass methods. To work around this, you can add the :exposes
and :exposes-methods
options to gen-class
.
:exposes
takes a map of the form {field {:get getter, :set setter}, ...}
. Each key is the name of a protected instance field of the superclass, the value specifies the names of public getter and setter methods that will be added to the generated class. You do not need to provide implementations for these methods; they are generated automatically.
:exposes-methods
takes a map of the form {super exposed, ...}
, where super
is the name of a superclass method, and exposed
is the name of a public method that will be added to the generated class. The exposed method calls the super method. You can use this feature when, for example, your implementation of a method needs to call the superclass version of the same method.
Java allows any class to be run as command-line executable, provided it has a method declared public static void main(String[] args)
. You can specify :main true
in gen-class
to add the static main method to your generated class. The function implementing this method should be called -main
(unless you changed the prefix). Rather than a single array argument, it will be called with however many arguments are present on the command line. An easy way to handle this is to define the function to take a variable number of arguments:
(gen-class ... :main true ...) (defn -main [& args] ...)
Once you have compiled a namespace with a :main
method, you can execute it at the command line like this:
java -cp ... your.class.name arguments...
Remember that your compiled .class files and the Clojure JAR must be on the classpath.
By default, any class generated with gen-class
will automatically load its implementing namespace from the classpath the first time it is used, just as if you had require
'd the namespace. If you are using some alternative code loading mechanism and you do not want the generated class to interfere, add the :load-impl-ns false
option to gen-class
.
gen-class
can appear as part of a namespace declaration in the ns
macro. In this case, it is written as (:gen-class options...)
. Within ns
, the :name
and :impl-ns
options default to the namespace being declared and :main
defaults to true
. Everything else is the same. However, remember that you need not limit yourself to one namespace per generated class. You could generate several classes, with different :prefix
options, and put all the method implementations in the same Clojure namespace.
If all you need is a program that can be run at the command line, you only need a -main
function and an ns
declaration containing (:gen-class)
, as in this example:
(ns com.example.app (:gen-class)) (defn -main [& args] (println "Hello, World!"))
When AOT-compiled into a directory named classes, this example can be run with the command:
java -cp classes:clojure.jar com.example.app
Clojure is not intended to replace the Java language. Rather, it is designed to augment the capabilities of the Java platform with a different style of programming. Newcomers to Clojure may dislike the intrusion of Java class and method names into their Clojure code, and rush to wrap every Java method call in a Clojure function. More experienced Clojure programmers appreciate the power offered by Java libraries and are comfortable mixing Java methods and Clojure functions. The world is too big to implement everything from scratch. Clojure takes advantage of the vast ecosystem of Java libraries and lives comfortably in a Java-based environment.
[6] http://java.sun.com/docs/books/tutorial/reflect/
[7] Method names as first-class functions has been suggested for a future version of Clojure.
[8] In contrast, the .NET Common Language Runtime has strongly-typed generics, which are more difficult to implement in a dynamically-typed language like Clojure. This was one reason for the choice of Java as the primary platform for Clojure.
[9] Java Classloaders, however, permit you to create multiple, independent execution contexts within a single JVM. Classloaders are an advanced Java topic outside the scope of this book.
[10] Early JVMs were implemented as bytecode interpreters. Modern JVM implementations use just-in-time compilation to convert the platform-independent Java bytecode into optimized machine code.
3.141.38.121