Cedric, hello.

On 2012 Jan 15, at 20:32, Cedric Greevey wrote:

> On Sun, Jan 15, 2012 at 6:52 AM, Norman Gray <norman.x.g...@gmail.com> wrote:

>> user=> (def m (proxy [java.util.HashMap] []
>>        (finalize []
>>          ;(proxy-super finalize)
>>          (prn "finalizing..."))
>>        (hashCode []
>>          99)))
>> #'user/m
>> user=> (.hashCode m)
>> 99
>> user=> (.finalize m)
>> IllegalArgumentException No matching field found: finalize for class 
>> user.proxy$java.util.HashMap$0  clojure.lang.Reflector.getInstanceField 
>> (Reflector.java:289)

> (TL;DR version: just skip to the end.)

(not too long at all; read avidly)

I'll work through this again tomorrow, but a couple of things occur to me now.

> Actually, ancestors expects a class rather than an instance, and
> returns nil for anything that's not a class.

Aha.  That's not particularly obvious from the 'ancestors' docs....  But I do 
see that in my case above, the new object appears to have some sort of subclass 
relationship with java.util.HashMap:

user=> (ancestors (.getClass m)) 
#{clojure.lang.IProxy java.lang.Object java.io.Serializable java.util.Map 
java.util.HashMap java.lang.Cloneable java.util.AbstractMap}

Excellent -- that makes sense.

> So, your problem is actually very simple: you tried to call a
> protected method from an unrelated class, which won't even work with
> reflection. At least, not normally.

Section 6.6.2 of the JLS mentions that "A protected member or constructor of an 
object may be accessed from outside the package in which it is declared only by 
code that is responsible for the implementation of that object"  (this is 
echoed in Sect 2.7.4 of the JVM spec). Now, the text which follows that seems 
to boil down to saying that subclass implementations can see protected instance 
fields or methods, which we all know, of course.

Also, you established that methods overridden in a proxy are public.

What those two statements appear to mean is that the overriding finalize method 
should be a public one, and that it should be able to call its super.finalize() 
method (of course, in this case the fact that finalize is public doesn't really 
matter, since what matters is that the GC can call finalise(), but we want to 
be able to call finalize() explicitly only to verify that it's there and doing 
what it should)

You conclude that:

> So, a proxy [...] will NOT override Object's finalize method, EVEN IF 
> explicitly
> told to do so and given an implementation for such an override.
> 
> Apparently it's treating that as a special case, either as an
> undocumented but intentional limitation of the proxy facility of
> clojure or else as a bug perhaps caused by the JVM treating finalize
> specially.

And further...

> Any further information is probably going to have to come, directly or
> indirectly, from someone who is knowledgeable about the intentions of
> the developers of the proxy macro, and not via the docs, since the
> docs, as you're aware, are mute on any special limitations on which
> protected methods can be overridden (they say only that they can be in
> general, but the user-supplied proxy code will not have access to
> protected fields of the proxied class).

There seem to be a couple of possibilities.

  * There's an edge-case of the legalese in JLS 6.6.2 or JVMS 2.7.4 or 5.4.4 
that is triggered by Clojure's implementation of the proxy class, and which is 
beyond my current forensic powers to spot.

  * Or there's a special case.  The 'generate-proxy' function in 
<https://github.com/clojure/clojure/blob/master/src/clj/clojure/core_proxy.clj> 
does mention "finalize" in a test which apparently excludes specifically that 
method from a "set of supers' non-private instance methods", but I can't work 
out quite what it's doing with those.  It _may_ be that the goal there is to 
automatically call super.methods for each method in the proxy, and I can see 
why one would want finalize() to be exempt from that, but if so, it may be this 
code which is, inadvertantly or not, preventing extending-classes adding 
finalizers.

The fact that (as you note) the proxy code is documented not to have access to 
protected members does suggest that the proxying class is at least not 
straightforwardly a subclass of the base class.  The Joy of Clojure mentions 
(p196) that "Clojure doesn't encourage implementation inheritance", that 
gen-class and proxy only provide "something like Java-style implementation 
inheritance", and  suggests (p210) that proxy classes trade flexibility for 
performance.  In this case, I can broadly understand why 'proxy' isn't 
producing what I think, or at least thought, it was producing (a 
straightforward subclass of the proxied class).  But in that case, I'm stumped, 
and don't know how I would go about overriding a finalize() method in an 
idiomatic and non-painful (ie non-gen-class) way.

Best wishes,

Norman


-- 
Norman Gray  :  http://nxg.me.uk



-- 
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

Reply via email to