problem calling methods on a Map.Entry

20 views
Skip to first unread message

.Bill Smith

unread,
Oct 21, 2007, 1:51:53 PM10/21/07
to Clojure
Has anyone else run into this? I know there are other ways to create
maps. The point is whether there's a way to inspect a Map.Entry.

user=> (def p (new java.util.Hashtable))
#<Var: user/p>
user=> (. p (put "a" "1"))
nil
user=> (. p (put "b" "2"))
nil
user=> p
{b=2, a=1}
user=> (. (first (seq (. p (entrySet)))) (getClass))
class java.util.Hashtable$Entry
user=> (. (first (seq (. p (entrySet)))) (getKey))
java.lang.IllegalAccessException: Class clojure.lang.Reflector can not
access a member of class java.util.Hashtable$Entry with modifiers
"public"
at sun.reflect.Reflection.ensureMemberAccess(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at clojure.lang.Reflector.invokeMatchingMethod(Reflector.java:37)
at clojure.lang.Reflector.invokeInstanceMethod(Reflector.java:25)
at clojure.lang.Compiler$InstanceMethodExpr.eval(Compiler.java:770)
at clojure.lang.Compiler.eval(Compiler.java:2555)
at clojure.lang.Compiler.main(Compiler.java:2728)

Thanks,
Bill

Rich Hickey

unread,
Oct 21, 2007, 3:32:56 PM10/21/07
to Clojure
java.util.Hashtable$Entry is private, and when using reflection to
find getKey it returns the method of the private class (rather than
the Map.Entry interface), which reflection won't allow you to call!

I've added special handling for this situation in Reflector.java -
it's in SVN now, and will be in the next release. With the change your
code works fine.

Thanks,

Rich

.Bill Smith

unread,
Oct 21, 2007, 10:31:14 PM10/21/07
to Clojure
Thanks Rich. That takes care of one aspect of the problem, but try
this:

(. (first (seq (. (. System (getProperties)) (entrySet)))) (toString))

It fails because toString() is a public method of a public super class
(java.lang.Object) of java.util.Hashtable$Entry. I think adding this
to the beginning of Reflector.getMethods() will help:

while (c != null && ! Modifier.isPublic(c.getModifiers())) {
c = c.getSuperclass();
}
if (c == null) {
return new ArrayList();
}

-Bill

.Bill Smith

unread,
Oct 21, 2007, 11:07:02 PM10/21/07
to Clojure
Actually, that fixes one problem and causes another. Try this
instead:

static public List getMethods(Class c, int arity, String name,
boolean getStatics){
ArrayList methods = new ArrayList();

Class origC = c;


while (c != null && ! Modifier.isPublic(c.getModifiers())) {
c = c.getSuperclass();
}

if (c != null) {
Method[] allmethods = c.getMethods();
for(int i = 0; i < allmethods.length; i++)
{
if (name.equals(allmethods[i].getName()) &&
Modifier.isStatic(allmethods[i].getModifiers()) == getStatics &&
allmethods[i].getParameterTypes().length == arity) {
methods.add(allmethods[i]);
}
}
}

if (!getStatics) {
for (Class xface : origC.getInterfaces()) {
for (Method m : xface.getMethods()) {
if (name.equals(m.getName())) {
if (m.getParameterTypes().length == arity) {
methods.add(m);
}
}
}
}
}

return methods;
}

Rich Hickey

unread,
Oct 22, 2007, 8:29:10 AM10/22/07
to Clojure
I've enhanced my fix to Reflector.invokeMatchingMethod to search
public superclasses as well as interfaces for a callable version of
the method.

user=> (. (first (seq (. (. System (getProperties)) (entrySet))))
(toString))
"java.runtime.name=Java(TM) 2 Runtime Environment, Standard Edition"

In case you weren't aware, that can be written as:

(str (first (.. System (getProperties) (entrySet))))

utilizing:
'..' - the chaining dot operator
first seqs its arg
str === toString

Fix is in SVN.

Thanks for the feedback,

Rich

Reply all
Reply to author
Forward
0 new messages