On Sun, Jan 15, 2012 at 5:26 PM, Norman Gray <norman.x.g...@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. -- 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