Java Class Factory

15 views
Skip to first unread message

lazy1

unread,
Dec 2, 2009, 11:15:45 PM12/2/09
to Clojure
Hello,

I'm trying to create a "factory" method for Java classes, however I'm
doing something wrong.

(import '(java.util Dictionary HashMap))

(def *containers* { :dict Dictionary :hash HashMap})
(defn new-container
[type]
(new (*containers* type)))

(def d (new-container :dict))

The above gives me
Exception in thread "main" java.lang.IllegalArgumentException: Unable
to resolve classname: (*containers* type) (t.clj:6)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:4599)
at clojure.lang.Compiler.analyze(Compiler.java:4405)
at clojure.lang.Compiler.analyze(Compiler.java:4366)
at clojure.lang.Compiler$BodyExpr$Parser.parse(Compiler.java:3942)
at clojure.lang.Compiler$FnMethod.parse(Compiler.java:3777)
at clojure.lang.Compiler$FnMethod.access$1100(Compiler.java:3654)
at clojure.lang.Compiler$FnExpr.parse(Compiler.java:3024)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:4590)
at clojure.lang.Compiler.analyze(Compiler.java:4405)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:4580)
at clojure.lang.Compiler.analyze(Compiler.java:4405)
at clojure.lang.Compiler.access$100(Compiler.java:35)
at clojure.lang.Compiler$DefExpr$Parser.parse(Compiler.java:373)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:4592)
at clojure.lang.Compiler.analyze(Compiler.java:4405)
at clojure.lang.Compiler.analyze(Compiler.java:4366)
at clojure.lang.Compiler.eval(Compiler.java:4646)
at clojure.lang.Compiler.load(Compiler.java:4972)
at clojure.lang.Compiler.loadFile(Compiler.java:4939)
at clojure.main$load_script__7423.invoke(main.clj:211)
at clojure.main$script_opt__7460.invoke(main.clj:263)
at clojure.main$main__7484.doInvoke(main.clj:338)
at clojure.lang.RestFn.invoke(RestFn.java:413)
at clojure.lang.Var.invoke(Var.java:359)
at clojure.lang.AFn.applyToHelper(AFn.java:173)
at clojure.lang.Var.applyTo(Var.java:476)
at clojure.main.main(main.java:37)
Caused by: java.lang.IllegalArgumentException: Unable to resolve
classname: (*containers* type)
at clojure.lang.Compiler$NewExpr$Parser.parse(Compiler.java:2243)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:4592)
... 26 more

What is the right way to do this?

Thanks,
--
Miki

Mike Hinchey

unread,
Dec 2, 2009, 11:40:58 PM12/2/09
to clo...@googlegroups.com
(new) tries to resolve the argument at compile-time, not runtime.  You need to spell out each (new class) in a cond.  You might write a macro to make it a little less verbose.

-Mike

ataggart

unread,
Dec 2, 2009, 11:57:59 PM12/2/09
to Clojure
What Mike said. Also, you might not even need to nor want to. There
may be a better option given a bit more info.

Dave M

unread,
Dec 2, 2009, 11:51:22 PM12/2/09
to Clojure


On Dec 2, 11:15 pm, lazy1 <miki.teb...@gmail.com> wrote:
> Hello,
>
> I'm trying to create a "factory" method for Java classes, however I'm
> doing something wrong.
>
> (import '(java.util Dictionary HashMap))
>
> (def *containers* { :dict Dictionary :hash HashMap})
> (defn new-container
>   [type]
>   (new (*containers* type)))
>
> (def d (new-container :dict))
>
> The above gives me
> Exception in thread "main" java.lang.IllegalArgumentException: Unable
> to resolve classname: (*containers* type) (t.clj:6)

"new" does not evaluate its arguments, so it is trying to interpret
the form "(*containers* type)" as a classname, which it cannot do.

Another problem with your example is that java.util.Dictionary is an
abstract class, and cannot be instantiated directly; of the two
classes you gave, you will only be able to create instances of
HashMap.

...

> What is the right way to do this?

Aside from the issue with Dictionary being abstract, I think you need
a macro to do what you want to do:

(defmacro new-container [type]
`(new ~(*containers* type)))

-Dave

>
> Thanks,
> --
> Miki

Jarkko Oranen

unread,
Dec 3, 2009, 3:37:36 AM12/3/09
to Clojure


On Dec 3, 6:15 am, lazy1 <miki.teb...@gmail.com> wrote:
> Hello,
>
> I'm trying to create a "factory" method for Java classes, however I'm
> doing something wrong.
>
> (import '(java.util Dictionary HashMap))
>
> (def *containers* { :dict Dictionary :hash HashMap})
> (defn new-container
>   [type]
>   (new (*containers* type)))
>
> (def d (new-container :dict))
<snip>

> What is the right way to do this?

As others have stated, you can't use new, but since you're storing
Class objects in a hashmap, you could simply call (.newInstance
(*containers* type))... of course, assuming that the Class is
instantiable.

--
Jarkko

sross

unread,
Dec 3, 2009, 4:40:31 AM12/3/09
to Clojure
On Dec 3, 4:15 am, lazy1 <miki.teb...@gmail.com> wrote:
> Hello,
> What is the right way to do this?

As people have mentioned, new is a special form which evaluates it's
arguments at
compile time and cannot be used with a dynamic class name.

Fortunately you can use the reflection API and create instance of your
class
using the newInstance method.

(defn new-container
[type]
(.newInstance (*containers* type)))


- sean

lazy1

unread,
Dec 3, 2009, 10:46:50 AM12/3/09
to Clojure
Hello All,

>> What is the right way to do this?
> (defn new-container
> [type]
> (.newInstance (*containers* type)))

> (defmacro new-container [type]
> `(new ~(*containers* type)))

Thanks! These are both great and also very educational.

The reason for this coding style is that I'm writing a wrapper around
webdriver, and I'd like a factory function to create a driver for a
specific browser.
(def *drivers* {
:firefox FirefoxDriver
:ie InternetExplorerDriver })

(defn new-driver
"create new driver"
[browser]
(.newInstance (*drivers* browser)))

Meikel Brandmeyer

unread,
Dec 3, 2009, 11:08:02 AM12/3/09
to Clojure
Hi,
And if nothing else works: there is always ye olde wrapper.

(defn firefox
[]
(FirefoxDriver.))

; This allows also for more elaborate setup if needed...
(defn internet-explorer
[]
(doto (IEDriver.)
(.setSomeOption 1)
(.andSomeOther "2")))

(def *drivers* {:firefox firefox :ie internet-explorer})

(defn new-driver
[browser]
((*drivers* browser)))

Sincerely
Meikel
Reply all
Reply to author
Forward
0 new messages