On Thu Aug  2 16:41 2012, David Greenberg wrote:
> Hi Clojurians,
> I'm finding myself far down the rabbit hole of gen-class. I am trying
> to generate a class that has a bunch of static methods, and each of
> those methods has many overloads of arities and types. Unfortunately,
> there is no interface--this class gets called through reflection in a
> legacy system.
> 
> The class's parameter types include primitives, primitive arrays, and Objects.
> 
> I am doing something like:
> 
> (gen-class
>   :name "my.odd.Class"
>   :main false
>   :methods [^{:static true} [-myfunc ["[Lint;" String] void]
>                  ^{:static true} [-myfunc ["[Ldouble;" int] Object]])
> 
> I found a post explaining that I could define method implementations
> with overloads by doing -methodName-arg1type-arg2type-arg3type, but
> when I try that I get an exception that the FileName is too long from
> the clojure compiler.
> 
> I can easily generate a map from signatures to implementations, but I
> need to generate the class with all the overloads.
> 
> Is there any way to do this? Should I resign myself to writing out a
> .java file, and compiling that?


Hello, David,

Well, gen-class certainly supports creating static methods with type
hints and overloaded arities.  An example using gen-class as part of ns:

(ns gen-class-test.StaticTest
  (:gen-class :methods [^:static [foo [ints int] String]
                        ^:static [foo [longs long] String]
                        ^:static [foo [chars char] String]
                        ^:static [foo [shorts short] String]
                        ^:static [foo [booleans boolean] String]
                        ^:static [foo [floats float] String]
                        ^:static [foo [doubles double] String]
                        ^:static [foo ["[Ljava.lang.Object;" Object] String]
                        ^:static [foo ["[Ljava.lang.String;" String] String]
                        ^:static [foo ["[Ljava.lang.String;" int] String]
                        ^:static [foo [boolean] String]
                        ^:static [foo [char] String]
                        ^:static [foo [short] String]
                        ^:static [foo [int] String]
                        ^:static [foo [long] String]
                        ^:static [foo [float] String]
                        ^:static [foo [double] String]
                        ^:static [foo [Object] String]
                        ^:static [foo [String] String]]
              :main false))

This will generate a class with the following signature:

public class gen_class_test.StaticTest extends java.lang.Object{
    public static {};
    public gen_class_test.StaticTest();
    public java.lang.Object clone();
    public int hashCode();
    public java.lang.String toString();
    public boolean equals(java.lang.Object);
    public static java.lang.String foo(int[], int);
    public static java.lang.String foo(long[], long);
    public static java.lang.String foo(char[], char);
    public static java.lang.String foo(short[], short);
    public static java.lang.String foo(boolean[], boolean);
    public static java.lang.String foo(float[], float);
    public static java.lang.String foo(double[], double);
    public static java.lang.String foo(java.lang.Object[], java.lang.Object);
    public static java.lang.String foo(java.lang.String[], java.lang.String);
    public static java.lang.String foo(java.lang.String[], int);
    public static java.lang.String foo(boolean);
    public static java.lang.String foo(char);
    public static java.lang.String foo(short);
    public static java.lang.String foo(int);
    public static java.lang.String foo(long);
    public static java.lang.String foo(float);
    public static java.lang.String foo(double);
    public static java.lang.String foo(java.lang.Object);
    public static java.lang.String foo(java.lang.String);
}

Now, when it comes to what this class is doing, it is calling the
Clojure function -foo with the arguments from the static method
invocation.  In particular:

1. All primitive arguments will be boxed.
2. The number of arguments for -foo must match the number of arguments
   for the static method invocation.  For multiple arities, you can have
   a variadic function or a function with multiple arities.
3. Unfortunately, there is no way to call a different function for each
   arity/type.

However, you can make -foo a multimethod.  The following works as an
implementation for the above:

(defmulti -foo
  (fn [& args]
    (apply vector (map class args))))

; one-arg invocations
(defmethod -foo [Boolean]   [_] "boolean")
(defmethod -foo [Character] [_] "char")
(defmethod -foo [Short]     [_] "short")
(defmethod -foo [Integer]   [_] "int")
(defmethod -foo [Long]      [_] "long")
(defmethod -foo [Float]     [_] "float")
(defmethod -foo [Double]    [_] "double")
(defmethod -foo [Object]    [_] "Object")
(defmethod -foo [String]    [_] "String")

; two-arg invocations
(defmethod -foo [(class (boolean-array 0)) Boolean]   [_ _] "booleans")
(defmethod -foo [(class (char-array 0))    Character] [_ _] "chars")
(defmethod -foo [(class (short-array 0))   Short]     [_ _] "shorts")
(defmethod -foo [(class (int-array 0))     Integer]   [_ _] "ints")
(defmethod -foo [(class (long-array 0))    Long]      [_ _] "longs")
(defmethod -foo [(class (float-array 0))   Float]     [_ _] "floats")
(defmethod -foo [(class (double-array 0))  Double]    [_ _] "doubles")
(defmethod -foo [(class (into-array [""])) String]    [_ _] "Strings")
(defmethod -foo [(class (to-array []))     Object]    [_ _] "Objects")
(defmethod -foo [(class (into-array [""])) Integer]   [_ _] "Strings + int")


Perhaps there is a better way to do it, but this is the best I could
come up with.  I hope it helps.

Sincerely,

Daniel

Attachment: signature.asc
Description: Digital signature

Reply via email to