Defining Java enums

331 views
Skip to first unread message

Stuart Sierra

unread,
May 26, 2008, 3:54:33 PM5/26/08
to Clojure
Hi all,
Is it possible to define Java enum types directly in Clojure or with
proxies/reflection?
Thanks,
-Stuart

Rich Hickey

unread,
May 27, 2008, 7:56:55 AM5/27/08
to Clojure


On May 26, 3:54 pm, Stuart Sierra <the.stuart.sie...@gmail.com> wrote:
> Hi all,
> Is it possible to define Java enum types directly in Clojure or with
> proxies/reflection?


Could you explain a bit about what you are trying to do? Is this to
define something for consumption by Java?

Rich

Stuart Sierra

unread,
May 27, 2008, 11:09:12 AM5/27/08
to Clojure
On May 26, 3:54 pm, Stuart Sierra <the.stuart.sie...@gmail.com> wrote:
> Is it possible to define Java enum types directly in Clojure or with
> proxies/reflection?

On May 27, 7:56 am, Rich Hickey <richhic...@gmail.com> wrote:
> Could you explain a bit about what you are trying to do? Is this to
> define something for consumption by Java?

Yes, I was thinking about Hadoop jobs in Clojure. Hadoop uses Java
enums for user-defined "counters". The API looks like this (
http://tinyurl.com/55fblv ):

void Reporter.incrCounter(Enum key, long amount)

And typical usage is in a nested class like this:

public class MyJob {
public static class MyMap implements Mapper {
static enum Counters { FOO, BAR, BAZ };
public void map(..., Reporter reporter) {
//...
reporter.incrCounter(Counters.FOO, 1);
}
}
}

Hadoop automatically sums the counters from all map tasks in the
cluster and reports the result as "MyJob$MyMap$Counters.FOO".

Looking at java.lang.Enum, it seems that this may suffice:

(def FOO (proxy [Enum] ["FOO" 1]))
...
(.incrCounter reporter FOO 1)

But I haven't that in Hadoop yet. Hadoop uses
Enum.getDeclaringClass(), which in the code above will be
clojure.lang.Proxy__####. I don't know if Hadoop assumes that this
class is statically-defined.

Of course, I could write a small Java class with all the counter names
I want to use, but that's so... static. :)

-Stuart

Rich Hickey

unread,
May 28, 2008, 7:35:29 AM5/28/08
to Clojure


On May 27, 11:09 am, Stuart Sierra <the.stuart.sie...@gmail.com>
wrote:
One thing to note is that all (proxy [Enum]...) will have the same
class.

> Of course, I could write a small Java class with all the counter names
> I want to use, but that's so... static. :)
>

Java names are static, that's why I've tried to keep them out of the
dynamic part of Clojure. But the new genclass stuff is the best of
both worlds - a way to generate the minimum necessary static stuff
(names and signatures) once, from Clojure, while retaining the ability
to dynamically define/redefine the logic behind the methods.

So, you have 3 options before going to Java:

(proxy [Enum] ...) - subject to the all-the-same-class caveat above

(gen-and-load-class your.class.Name :extends Enum ...) - to get unique
names, which will be available for use in Clojure code

(gen-and-save-class your.class.Name :extends Enum ...) - to spit
a .class file onto disk which can be loaded by Hadoop if it needs to
create instances of your class.

Rich

Stuart Sierra

unread,
May 29, 2008, 4:10:06 PM5/29/08
to Clojure
On May 26, 3:54 pm, Stuart Sierra <the.stuart.sie...@gmail.com> wrote:
> Is it possible to define Java enum types directly in Clojure or with
> proxies/reflection?

On May 28, 7:35 am, Rich Hickey <richhic...@gmail.com> wrote:
> One thing to note is that all (proxy [Enum]...) will have the same
> class.
<snip>
> So, you have 3 options before going to Java:
>
> (proxy [Enum] ...) - subject to the all-the-same-class caveat above
>
> (gen-and-load-class your.class.Name :extends Enum ...) - to get unique
> names, which will be available for use in Clojure code
<snip>

Thanks, Rich. Both of these work:

(def counter1 (proxy [Enum] ["Counter1" 1]))
(def counter2 (proxy [Enum] ["Counter2" 2])) ; funny class name

;; OR

(gen-and-load-class "my.package.Counters" :extends Enum)
(def counter1 (new my.package.Counters "Counter1" 1))
(def counter2 (new my.package.Counters "Counter2" 2))

I noticed the genclass stuff in SVN a while ago -- was wondering when
you would bring it up on the list!

For fun, I even made a macro:

(defmacro defenum [class & symbols]
(try (. Class (forName (str class)))
(catch java.lang.ClassNotFoundException e
(gen-and-load-class (str class) :extends java.lang.Enum)))
(cons 'do
(map (fn [sym val]
`(def ~symbol (new ~class ~(str sym) ~val)))
symbols (iterate inc 1))))

The try/forName bit is to prevent the class from being loaded more
than once. Now I can do:

(defenum my.package.Counters Counter1 Counter2)

This does, of course, break every type safety constraint on Java's
enums. Long live dynamic typing!

(.isEnum (class Counter1)) returns false, but otherwise it's a real
Java Enum.
Reply all
Reply to author
Forward
0 new messages