问题
I know the basics of clojure/java interop: calling java from clojure and vice versa. However, I was not able to return a typed collection from clojure to java. I am trying to see something of that nature List<TypedObject>
from the java code which is calling into clojure.
Java Object:
public class TypedObject {
private OtherType1 _prop1;
public OtherType1 getProp1() {
return _prop1;
}
public void setProp1(OtherType1 prop1) {
_prop1 = prop1;
}
}
CLojure method:
(defn -createListOfTypedObjects
"Creates and returns a list of TypedObjects"
[input]
;Do work here to create and return list of TypedObjects
[typedObj1, typedObj2, typedObj3])
(:gen-class
:name some.namespace
:methods [createListofTypedObjects[String] ????])
Let us consider that I am writing an API using clojure, which is to be distributed as a jar file, to be used from java. My question was really how to what to pass in place of the ???? questions marks above inside the :gen-class for AOT, so that a programmer writing a piece of code in java using my api, can have the appropriate intellisense / code completion (i.e.: createListofTypedObjects() returns List<TypedObject>
) from within eclipse for example.
回答1:
The others are right that Clojure doesn't ensure the types of elements in returned collections, etc. (Actually, the JVM doesn't ensure the types of elements in collections, either – that's handled entirely by javac.)
However, I can see the value of providing an API to other Java programmers that specifies an interface that declares that return values (or parameters) parameterized in various ways; this is especially attractive if one is looking to use Clojure in an existing Java environment without making waves.
This currently requires a two step process:
- define a separate interface (in Java!) that specifies the parameterized types as you like
- define your
gen-class
namespace (orproxy
orreify
instance) such that it implements that interface
(Clojure does provide a definterface
form that would allow you to avoid the separate Java interface definition, but definterface
, just like the rest of Clojure, does not provide for specifying parameterized types. Maybe someday... :-))
e.g.
public interface IFoo {
List<TypedObject> createListOfTypedObjects ();
}
and then your gen-class namespace:
(ns your.ns.FooImpl
(:gen-class
:implements [IFoo]))
(defn -createListOfTypedObjects
[]
[typedObj1, typedObj2, typedObj3])
When your users create instances of FooImpl
, they'll e.g. get code completion indicating that the method returns List<TypedObject>
rather than Object
or the unparameterized List
type.
If you're using sane build tools (e.g. maven, gradle, or properly-configured ant), then you can put the Java interface in your Clojure project, and the cross-language dependency will be taken care of.
回答2:
If you're trying to pass something like List<String>
to a java method, then you don't need to worry about it. The type parameter (e.g., String
) is only used to by the javac compiler, so any List
will work just fine at runtime.
On the other hand if you're trying to pass an array of a particular object type (e.g., String[]
), then you can use the various -array
functions:
user=> (make-array String 10) ; an empty String array
#<String[] [Ljava.lang.String;@78878c4c>
user=> (into-array ["foo" "bar"]) ; array type inferred from first element
#<String[] [Ljava.lang.String;@743fbbfc>
user=> (into-array Number [1.2 5 7N]) ; explicit type array
#<Number[] [Ljava.lang.Number;@7433b121>
回答3:
You don't need to worry about generics (typed collections) in Clojure. Generics are really just type hints to the Java compiler. In a running Java program, List<String>
is effectively the same as List<Object>
.
So, for example, a Clojure vector containing Strings is already a List<String>
with no conversion needed.
来源:https://stackoverflow.com/questions/3688730/how-to-pass-a-typed-collection-from-clojure-to-java