1

I'm using a Java library from Clojure. The library cookbook describes a static method that is used as follows:

List<String> myList = new ArrayList<String>();
// next, add values to list, then:
myResults = JavaClass.staticMethod(myList);

Initially, I thought I would be able to use the method from Clojure by doing the following:

(-> ["vector" "of" "strings"]
  java.util.ArrayList.
  JavaClass/staticMethod)

But when I do this I get the error message "CompilerException java.lang.IllegalArgumentException: No matching method: staticMethod".

The only thing I can see that I'm doing wrong is that in the cookbook example, the ArrayList is being declared as type List, and although ArrayList implements the List interface I'm not explicitly doing that in the Clojure code. My Java-fu is not that strong, so am I correctly understanding how method signatures work in Java?

EDIT: As one of the answers below alludes to, the method is variadic. It has the form

public static List<String> staticMethod(List<String> args, Object... moreArgs) {
...
}
stuwest
  • 910
  • 1
  • 6
  • 14

3 Answers3

4

If the java library you're using actually declares the method like so:

public static Whatever staticMethod(List<String> xs) {...}

then what you're doing is correct (and in fact you can even skip the j.u.ArrayList call since vectors implement List too). So, either you are doing something different from what you say you're doing, or the method is declared differently from how you're attempting to use it.

My bet is on the latter: is there, perhaps, a varargs (Foo... xs) argument you didn't mention? If so, see something like How to handle java variable length arguments in clojure? for the right approach. If not, well, please paste the exact signature of the method you're trying to call, and maybe someone will figure it out.

Community
  • 1
  • 1
amalloy
  • 89,153
  • 8
  • 140
  • 205
2

My usual method for these situations is to use clojure.reflect/refelct on the class and see what it's really expect

main> (-> [1 2 3]
          java.util.ArrayList.
          java.util.Arrays/copyOf)
CompilerException java.lang.IllegalArgumentException: No matching method: copyOf, compiling:(*cider-repl api*:49:24) 

main> (-> java.util.Arrays
          clojure.reflect/reflect
          clojure.pprint/pprint)
{:bases #{java.lang.Object},
 :flags #{:public},
 :members
 #{{:name stream,
    :return-type java.util.stream.Stream,
    :declaring-class java.util.Arrays,
    :parameter-types [java.lang.Object<>],
    :exception-types [],
    :flags #{:public :static}}

.... lots more methods ...

   {:name copyOf,
    :return-type java.lang.Object<>,
    :declaring-class java.util.Arrays,
    :parameter-types [java.lang.Object<> int java.lang.Class],
    :exception-types [],
    :flags #{:public :static}}

.... lots more ....
Arthur Ulfeldt
  • 90,827
  • 27
  • 201
  • 284
1

As user amalloy pointed out in their answer, you can't just ignore Java varargs arguments when calling from Clojure. When I changed my code to:

(-> ["vector" "of" "strings"]
 (JavaClass/staticMethod (into-array Object [nil])))

everything worked as intended.

stuwest
  • 910
  • 1
  • 6
  • 14