Proxies and overriding protected methods

1,365 views
Skip to first unread message

Norman Gray

unread,
Jan 15, 2012, 6:52:49 AM1/15/12
to clo...@googlegroups.com

Greetings.

I'm not understanding proxies. Can anyone advise me what I'm missing?

I try the following:

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)
user=> (ancestors m)
nil
user=>

(the 'proxy-super' is commented out, just to reassure myself that its presence is not somehow the root of the problem)

I'm afraid that the documentation of the 'proxy' macro is too terse for me to work out what's supposed to be happening here.

The documentation says that 'proxy' "creates a instance of a proxy class that implements the named class/interface(s) by calling the supplied fns." That suggests (without quite saying so) that 'm' should be an extension of class java.util.HashMap, in this case, and this is corroborated by the remark that "If a method fn is not provided for a class method, the _superclass_ methd will be called" (my emphasis, note the docs' misspelling of 'methd'). However, the null result of (ancestors m) suggests instead that 'm' extends only Object, and that it handles _all_ of its method calling by delegation through reflection on the class being proxied.

It's the second interpretation that seems to explain why I can't override protected method 'finalize' (although that would suggest that I could create a new method 'finalize', which shadows Object.finalize without overriding it, and doesn't really explain why I'm seeing this error). That said, however, the docs stress that "Note that while method fns can be provided to override protected methods...", which seems to say that what I'm trying to do, here, should be possible. The rest of that sentence seems to suggest that something more complicated is happening, without revealing quite what.

Also Fogus and Houser (in The Joy of Clojure, p211) give an example of using 'proxy', in which they illustrate a function proxying FilterOutputStream, and say that "The proxy returned by screaming-filter extends the Java class java.io.FilterOutputStream". That seems to suggest that the proxy is indeed a subclass of the thing it's proxying.

Finally, the on-line discussion of gen-class at <http://clojure.org/compilation> mentions that one of the facilities it provides is "Exposing inherited protected members". However the description of gen-class, there, does strongly suggest that it's a heavyweight, exotic thing, intended for fuller JVM interop rather than everyday use, and is telling me, in effect, "these aren't the macros you're looking for; move along". It sounds as if getting involved in gen-class in this context would be an unidiomatic way of doing something fairly simple, namely producing an anonymous extension of a class which overrides a protected method.

What am I missing?

Thanks for any pointers. Best wishes,

Norman


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

Cedric Greevey

unread,
Jan 15, 2012, 3:32:55 PM1/15/12
to clo...@googlegroups.com
On Sun, Jan 15, 2012 at 6:52 AM, Norman Gray <norman...@gmail.com> wrote:
>
> Greetings.
>
> I'm not understanding proxies. Can anyone advise me what I'm missing?
>
> I try the following:
>
> 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)
> user=> (ancestors m)
> nil
> user=>
>
> (the 'proxy-super' is commented out, just to reassure myself that its presence is not somehow the root of the problem)
>
> I'm afraid that the documentation of the 'proxy' macro is too terse for me to work out what's supposed to be happening here.

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

It's actually the case that most other clojure.core docstrings are
even terser. :)

> The documentation says that 'proxy' "creates a instance of a proxy class that implements the named class/interface(s) by calling the supplied fns." That suggests (without quite saying so) that 'm' should be an extension of class java.util.HashMap, in this case, and this is corroborated by the remark that "If a method fn is not provided for a class method, the _superclass_ methd will be called" (my emphasis, note the docs' misspelling of 'methd'). However, the null result of (ancestors m) suggests instead that 'm' extends only Object, and that it handles _all_ of its method calling by delegation through reflection on the class being proxied.

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

user=> (def x (proxy [Object][] (finalize [] (println "foo!"))))
#'user/x
user=> (def y (.getClass x))
#'user/y
user=> (ancestors y)
#{java.lang.Object clojure.lang.IProxy}

Presto: two Class objects representing superclasses/interfaces
inherited by the class y of the proxy object x.

Furthermore:

user=> (seq (.getMethods y))
(#<Method public void
user.proxy$java.lang.Object$0.__initClojureFnMappings(clojure.lang.IPersistentMap)>
#<Method public void
user.proxy$java.lang.Object$0.__updateClojureFnMappings(clojure.lang.IPersistentMap)>
#<Method public clojure.lang.IPersistentMap
user.proxy$java.lang.Object$0.__getClojureFnMappings()>
#<Method public boolean user.proxy$java.lang.Object$0.equals(java.lang.Object)>
#<Method public java.lang.String user.proxy$java.lang.Object$0.toString()>
#<Method public int user.proxy$java.lang.Object$0.hashCode()>
#<Method public java.lang.Object user.proxy$java.lang.Object$0.clone()>
#<Method public final native void java.lang.Object.wait(long) throws
java.lang.InterruptedException>
#<Method public final void java.lang.Object.wait() throws
java.lang.InterruptedException>
#<Method public final void java.lang.Object.wait(long,int) throws
java.lang.InterruptedException>
#<Method public final native java.lang.Class java.lang.Object.getClass()>
#<Method public final native void java.lang.Object.notify()>
#<Method public final native void java.lang.Object.notifyAll()>)

Note: no finalize.

Javadocs for Object show finalize, so it's part of Object and should
at least be inherited even if not overridden. The getMethods call
obviously shows inherited, unoverridden methods as it shows several
final methods of Object such as notifyAll. What is going on here?

Javadocs for Class.getMethod show this: "Returns an array containing
Method objects reflecting all the public member methods..."

So, nonpublic methods aren't shown. Proxy seems to provide a public
override of clone automatically, but finalize obviously remained
protected, rather than the proxy override in x being public rather
than protected.

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.

Indeed:

user=> (.finalize (Object.))
#<IllegalArgumentException java.lang.IllegalArgumentException: No
matching field found: finalize for class java.lang.Object>

Same error, with a plain Object.

A different way of calling the method produces a more accurate message:

user=> (def z (.getDeclaredMethod Object "finalize" (into-array Class [])))
#'user/z

Now we have a method handle for Object's finalize method.

user=> (.invoke z (Object.) (into-array Object []))
#<IllegalAccessException java.lang.IllegalAccessException: Class
user$eval673 can not access a member of class java.lang.Object with
modifiers "protected">

At the JVM level, your REPL evaluations become little classes with
names like user$eval673. From a given class, such as user$eval673, you
can normally only access protected members of an instance of that
class. This includes inherited ones, unlike with private members, but
the class of the object you're invoking a protected method of must be
the same as or a subclass of the class containing the method invoking
the protected method -- or else the class containing the protected
method (not just a nonoverriding subclass) must be in the same package
as the class containing the calling code.

In this case, though user$eval673 is an Object, the (Object.) is *not*
a user$eval673, and furthermore the package it's in (the default
package, in this case) is not the package Object is in (java.lang). So
the call fails.

But ...

user=> (.setAccessible z true)
nil
user=> (.invoke z (Object.) (into-array Object []))
nil

Look, ma! No exception!

But even this isn't the whole story with your proxy's finalize method.

user=> (def w (.getDeclaredMethod y "finalize" (into-array Class [])))
#<NoSuchMethodException java.lang.NoSuchMethodException:
user.proxy$java.lang.Object$0.finalize()>

What the hell?

user=> (seq (.getDeclaredMethods Object))
(#<Method protected void java.lang.Object.finalize() throws java.lang.Throwable>
#<Method public final native void java.lang.Object.wait(long) throws
java.lang.InterruptedException>
#<Method public final void java.lang.Object.wait() throws
java.lang.InterruptedException>
#<Method public final void java.lang.Object.wait(long,int) throws
java.lang.InterruptedException>
#<Method public boolean java.lang.Object.equals(java.lang.Object)>
#<Method public java.lang.String java.lang.Object.toString()>
#<Method public native int java.lang.Object.hashCode()>
#<Method public final native java.lang.Class java.lang.Object.getClass()>
#<Method protected native java.lang.Object java.lang.Object.clone()
throws java.lang.CloneNotSupportedException>
#<Method private static native void java.lang.Object.registerNatives()>
#<Method public final native void java.lang.Object.notify()>
#<Method public final native void java.lang.Object.notifyAll()>)

Unlike getMethods, getDeclaredMethods shows non-public methods (while
omitting unoverridden inherited ones). Object.finalize is shown, as
well as Object.clone and the undocumented private method
Object.registerNatives.

user=> (seq (.getDeclaredMethods y))
(#<Method public void
user.proxy$java.lang.Object$0.__initClojureFnMappings(clojure.lang.IPersistentMap)>
#<Method public void
user.proxy$java.lang.Object$0.__updateClojureFnMappings(clojure.lang.IPersistentMap)>
#<Method public clojure.lang.IPersistentMap
user.proxy$java.lang.Object$0.__getClojureFnMappings()>
#<Method public boolean user.proxy$java.lang.Object$0.equals(java.lang.Object)>
#<Method public java.lang.String user.proxy$java.lang.Object$0.toString()>
#<Method public int user.proxy$java.lang.Object$0.hashCode()>
#<Method public java.lang.Object user.proxy$java.lang.Object$0.clone()>)

The proxy provided three methods that are clearly implementation
details of Clojure proxies, as well as default implementations of
equals, toString, hashCode, and clone (and made clone public). But it
seems that the finalize method was *not* overridden, despite being in
the proxy code.

Yet the proxy docs clearly indicate it should be possible. Maybe
finalize is a special case? A few more tests:

user=> (def p (proxy [java.util.Observable] [] (clearChanged []
(println "foo!"))))
#'user/p
user=> (seq (.getDeclaredMethods (.getClass p)))
(#<Method public void
user.proxy$java.util.Observable$0.__initClojureFnMappings(clojure.lang.IPersistentMap)>
#<Method public void
user.proxy$java.util.Observable$0.__updateClojureFnMappings(clojure.lang.IPersistentMap)>
#<Method public clojure.lang.IPersistentMap
user.proxy$java.util.Observable$0.__getClojureFnMappings()>
#<Method public void
user.proxy$java.util.Observable$0.addObserver(java.util.Observer)>
#<Method public void user.proxy$java.util.Observable$0.clearChanged()>
#<Method public int user.proxy$java.util.Observable$0.countObservers()>
#<Method public void
user.proxy$java.util.Observable$0.deleteObserver(java.util.Observer)>
#<Method public void user.proxy$java.util.Observable$0.deleteObservers()>
#<Method public boolean user.proxy$java.util.Observable$0.hasChanged()>
#<Method public void user.proxy$java.util.Observable$0.notifyObservers()>
#<Method public void
user.proxy$java.util.Observable$0.notifyObservers(java.lang.Object)>
#<Method public void user.proxy$java.util.Observable$0.setChanged()>
#<Method public boolean
user.proxy$java.util.Observable$0.equals(java.lang.Object)>
#<Method public java.lang.String user.proxy$java.util.Observable$0.toString()>
#<Method public int user.proxy$java.util.Observable$0.hashCode()>
#<Method public java.lang.Object user.proxy$java.util.Observable$0.clone()>)

The protected clearChanged method of java.util.Observable can be
overridden, it seems, and doing so makes it public in the proxy class.
On top of that, however, the protected method setChanged was *also*
made public in the proxy, though there was no explicit override of
setChanged.

Another test reveals some more:

user=> (def q (proxy [javax.swing.Box] [1] (getAccessibleContext []
(println "foo!"))))
#'user/q

This ought to inherit JComponent's protected method
fireVetoableChange, but it does not override it.

user=>
(seq (.getDeclaredMethods (.getClass q)))
(#<Method public void
user.proxy$javax.swing.Box$0.__initClojureFnMappings(clojure.lang.IPersistentMap)>
#<Method public void
user.proxy$javax.swing.Box$0.__updateClojureFnMappings(clojure.lang.IPersistentMap)>
#<Method public clojure.lang.IPersistentMap
user.proxy$javax.swing.Box$0.__getClojureFnMappings()>
#<Method public java.awt.Component
user.proxy$javax.swing.Box$0.add(java.awt.Component,int)>
#<Method public void
user.proxy$javax.swing.Box$0.add(java.awt.Component,java.lang.Object)>
#<Method public java.awt.Component
user.proxy$javax.swing.Box$0.add(java.lang.String,java.awt.Component)>
#<Method public java.awt.Component
user.proxy$javax.swing.Box$0.add(java.awt.Component)>
#<Method public void user.proxy$javax.swing.Box$0.add(java.awt.PopupMenu)>
#<Method public void
user.proxy$javax.swing.Box$0.add(java.awt.Component,java.lang.Object,int)>
...

#<Method public void
user.proxy$javax.swing.Box$0.fireVetoableChange(java.lang.String,java.lang.Object,java.lang.Object)>
...)

The proxy did create a public override of the method.

So, a proxy WILL override protected methods that are directly in the
class being proxied -- moreover it will make each of these public,
whether or not the proxy specifically is told to include an overriding
implementation of it.

It WILL override most protected methods that are inherited from higher
up the hierarchy, but not overridden in the class being proxied, again
at the very least making each of them public even if the proxy isn't
specifically given an implementation.

This DOES include Object's clone method.

But, it 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.

Any other hypothesis fails on the data:

That it treats Object specially as a whole -- but clone is overridable.

That it only allows overriding protected methods that have an
implementation in the immediate superclass -- but Object.finalize
isn't overridable even in a direct proxy of Object, and
JComponent.fireVetoableChange is overridable in a proxy of Box, which
does not override it.

That it does not allow overriding protected methods at all, with a
special-case exception for Object.clone -- but it allows overriding of
JComponent.fireVetoableChange, Observer.clearChanged, and
Observer.setChanged as well.

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

Norman Gray

unread,
Jan 15, 2012, 5:26:07 PM1/15/12
to Cedric Greevey, clo...@googlegroups.com

Cedric, hello.

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

> On Sun, Jan 15, 2012 at 6:52 AM, Norman Gray <norman...@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.

Chris Perkins

unread,
Jan 16, 2012, 6:12:34 AM1/16/12
to clo...@googlegroups.com
Norman,

Finalize is a protected method, so you can't call it. You get the same error trying to call finalize on anything - it has nothing to do with proxy.

user> (.finalize (Object.))
No matching field found: finalize for class java.lang.Object

- Chris


Chris Perkins

unread,
Jan 16, 2012, 6:23:47 AM1/16/12
to clo...@googlegroups.com
Oops, ignore that - haven't had my morning coffee yet :)  I see that you are trying to make finalize accessible by overriding it. Agreed that it seems like it should work.  Assuming that proxy can successfully override other protected methods, you should probably log a bug.

Norman Gray

unread,
Jan 16, 2012, 6:38:27 AM1/16/12
to clo...@googlegroups.com

Chris, hello.

On 2012 Jan 16, at 11:23, Chris Perkins wrote:

> Oops, ignore that - haven't had my morning coffee yet :) I see that you
> are trying to make finalize accessible by overriding it.

Indeed!

> Agreed that it
> seems like it should work. Assuming that proxy can successfully override
> other protected methods, you should probably log a bug.

Righto. I presume the Clojure.core bugparade is at <http://dev.clojure.org/jira/browse/CLJ>, yes?

All the best,

Cedric Greevey

unread,
Jan 16, 2012, 12:56:27 PM1/16/12
to clo...@googlegroups.com
On Sun, Jan 15, 2012 at 5:26 PM, Norman Gray <norman...@gmail.com> wrote:
> 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.

Also, that it overrides all of them whether you provide an
implementation or not. A few tests seem to confirm that the default
implementations are just call-super implementations (when the
overridden method is concrete, anyway). More on that anon.

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

Further down in the weeds than I've yet gotten while investigating this.

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

Seems likely then that it's either an intentional limitation, or a bug
resulting from preventing the finalizer from automatically starting
with super.finalize(). A proper nontrivial finalizer should be try {
do something; } finally { super.finalize(); } so your cleanup goes
first, then your superclass's -- reverse order of initialization; so
it shouldn't *start* with finalize. A proper clojure finalizer would
have to be (finalize [] (try (do something) (finally (proxy-super
finalize)))). Though finalizers are icky, they probably *should* be
allowed, for completeness' sake. I'm inclined to regard this as a bug,
unless someone posts with a convincing explanation why clojure proxies
having nontrivial finalizers would be a very bad idea.

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

As I understand it, there are two pieces to the proxy.

One is a subclass of the specified base class and/or interface(s). The
proxy object is of this class; as you saw, your HashMap derived proxy
had an (ancestors (.getClass x)) that contained HashMap among other
things.

The other component is a map of clojure function objects. The proxy
object's methods just look things up in this map and invoke them. The
map can be updated later, with update-proxy, and the existing proxy
object's methods will change behavior correspondingly, something not
otherwise possible. That's why there's a default call-super
implementation of every method, even if you don't override it when you
first call proxy, because if there wasn't, you couldn't later override
it with update-proxy.

It also lets you always call protected methods that you didn't override:

user=> (.toString (proxy [Object] [] (toString [] (do (.clone this) "boo!"))))
#<CloneNotSupportedException java.lang.CloneNotSupportedException:
user$java.lang.Object$0>

If the proxy didn't make clone public, the clojure function could not
call it in the do up above, because it's not a method of the proxy
class itself, but of its own class, as outlined above. And to prove
it:

user=> (.toString (proxy [Object] [] (toString [] (.getClassName
(first (.getStackTrace (Exception.)))))))
"user$eval940$fn__941"

The class user$eval940 is a class with a method that executes the
compiled (.toString ...) code. When that is entered at the REPL, it's
compiled, that class (or one with a similar name) is created, and that
method is then invoked to actually execute what you entered at the
REPL.

The class user$eval940$fn__941 is a nested class of that class, and
will be the body of the toString method specified for the proxy.

The proxy itself, as noted above, has a toString override that looks
up toString in some clojure map, pulls out an instance of
user$eval940$fn__941, and calls the no-argument invoke method, which
contains the compiled toString body.

So, the clone method of the proxy's class, user$java.lang.Object$0, is
being called from the class user$eval940$fn__941. In this case, it
would have worked anyway since both are in the default package, but if
they weren't in the same Clojure namespace (or both in single-segment
namespaces) it wouldn't work if the proxy class's clone method had
stayed protected instead of being made public. The scenario wheres
that would occur include:

Updating the proxy with update-proxy from a different namespace than
the proxy was created in.

The protected method is called by a helper function, which is called
from the proxy body, and the function and the proxy body are not in
the same namespace.


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

Even then, a proxy method call is actually a Java call, a map lookup,
and another Java call, so that proxies can be updated after creation.
The newer reify is even more performant, but lacks this dynamic
updatability.

Proxy is plenty fast enough for a common use: GUI listener objects.
The slight added call overhead before your ActionListener code is
executing is nowhere near enough for you to have finished that mouse
click motion with your hand first, let alone made another click, or
keystroke, or whatever. :)

Anything called by a Java library in a tight loop via an interface,
though, you might prefer reify for. Reify has some other features,
too, and probably some other limitations. Though there's a sizable
overlap region where either can be used with acceptable results.

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

Until and unless it proves to be a bug and gets fixed, it looks like
you'll need to use gen-class, or maybe even dirty your hands writing
actual Java source code. Sorry.

You might want to try reify and deftype first, though, before you give
up. One of those might let you override finalize.

There is, by the way, *at least* one bug here and possibly two.

If the proxy-can't-override-finalize behavior is unintentional, then
that's the bug.

On the other hand, if it *is* intentional, its undocumented nature is
one bug and its symptom being a silent failure rather than something
like #<CompilerException: cannot override finalize with proxy, use
gen-class/whatever instead> is another bug.

Norman Gray

unread,
Jan 16, 2012, 5:29:09 PM1/16/12
to clo...@googlegroups.com

Cedric, hello.

Thanks for these further notes. Following your remarks and Chris's, I've logged this as a bug at <http://dev.clojure.org/jira/browse/CLJ-911>

I've added a few further comments below.

On 16 Jan 2012, at 17:56, Cedric Greevey wrote:

> A proper clojure finalizer would
> have to be (finalize [] (try (do something) (finally (proxy-super
> finalize)))). Though finalizers are icky, they probably *should* be
> allowed, for completeness' sake. I'm inclined to regard this as a bug,
> unless someone posts with a convincing explanation why clojure proxies
> having nontrivial finalizers would be a very bad idea.

I was about to disagree with you about finalizers, but on reflection I won't.

I'm in Clojure, having stumbled over the border from Racket, which is a lot more comfortable with finalizers, via notions of custodians and wills. In the application I'm working on, I believe I can eventually make (JVM) finalizers robust, and in this case there aren't the resource-exhaustion problems that convincingly argue against the strategy in general; but since the JLS goes out of its way to make only the most evasive guarantees about finalizer behaviour, I think one can say that finalizers just aren't JVM-idiomatic, matteradamn what the brackets look like, and conclude that I should find another pattern. I feel a with-* macro coming on....

> As I understand it, there are two pieces to the proxy.
>
> One is a subclass of the specified base class and/or interface(s). The
> proxy object is of this class; as you saw, your HashMap derived proxy
> had an (ancestors (.getClass x)) that contained HashMap among other
> things.
>
> The other component is a map of clojure function objects. The proxy
> object's methods just look things up in this map and invoke them. The
> map can be updated later, with update-proxy, and the existing proxy
> object's methods will change behavior correspondingly, something not
> otherwise possible. That's why there's a default call-super
> implementation of every method, even if you don't override it when you
> first call proxy, because if there wasn't, you couldn't later override
> it with update-proxy.

This is very valuable background.

> Anything called by a Java library in a tight loop via an interface,
> though, you might prefer reify for. Reify has some other features,
> too, and probably some other limitations. Though there's a sizable
> overlap region where either can be used with acceptable results.

It's not clear from the docs what 'reify' returns, but it seems to accept only protocols, interfaces or java.lang.Object. It does appear therefore that the only way of extending (in JVM terms) a class is through bells-and-whistles-and-gongs gen-class.

Best wishes, to all,

Cedric Greevey

unread,
Jan 16, 2012, 5:43:02 PM1/16/12
to clo...@googlegroups.com
On Mon, Jan 16, 2012 at 5:29 PM, Norman Gray <norman...@gmail.com> wrote:
>> Anything called by a Java library in a tight loop via an interface,
>> though, you might prefer reify for. Reify has some other features,
>> too, and probably some other limitations. Though there's a sizable
>> overlap region where either can be used with acceptable results.
>
> It's not clear from the docs what 'reify' returns, but it seems to accept only protocols, interfaces or java.lang.Object.  It does appear therefore that the only way of extending (in JVM terms) a class is through bells-and-whistles-and-gongs gen-class.

That'd be one of the other limitations of reify. In your particular
HashMap-extending case it wouldn't work, though you could implement
Map and delegate to a closed-over HashMap.

Like proxy, though, reify returns an instance rather than creating a
class for later instance-creation (or for static method calls);
deftype, defrecord, and gen-class do that and defrecord makes Map-like
classes (but immutable ones) (you can still add other behavior,
including overriding methods from Object and such).

Think of proxy and reify as somewhat like Java anonymous inner
classes, and deftype, defrecord, and gen-class as somewhat like
non-anonymous Java classes, with defrecord additionally inheriting
many of the behaviors of Clojure's map type.

Reply all
Reply to author
Forward
0 new messages