I ended up digging deep through gen-class, and I learned about an interesting, undocumented feature that solves this problem:
You can, in fact, overload methods of the same arity on type, and here's how: Each method you define in gen-class tries to lookup a corresponding var in the impl-ns of the form {impl-ns}/{prefix}{method-name} However, if the method is overloaded on type, gen-class first looks up a var of the form {impl-ns}/{prefix}{method-name}{typesig}, and only if that fails does it use the default var. typesig is constructed in the following way: (str (interleave (repeat \-) (map typesig-name types)) where types is the vector of types passed to the method declaration. Finally, here's a way to define typesig-name (and I'm assuming all arguments are Classes) (defn typesig-name [c] (cond (.isArray c) (str (typesig-name (.getComponentType c)) "<>") (.isPrimitive c) (comment this should give "int", "float", "double", "long", etc) (.getSimpleName c))) If you provide vars with those names, you can overload by arity. To recap, these are the quirks: 1) If you don't overload a method, you must provide the implementation in the var of the same name. 2) If you do overload the arity, you can optionally provide the implementation in the specially named vars, but if they don't exist, they'll fall back to vars of the same name. 3) The overload vars have dash-separated type signatures included in their name, where primitives are written like in java, arrays end in "<>", and you only include the simple name of the classes. Whew... p.s. unfortunately, clojure still boxes the arguments into these function no matter what. So this is a dispatch optimization, not a boxing optimization (or, in my case, allows me to generate the correct interop forms). On Friday, August 3, 2012 10:11:06 AM UTC-4, Daniel Solano Gómez wrote: > > 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 > -- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en