On Mar 23, 9:55 am, ronen <nark...@gmail.com> wrote:
> [... what] I actually need is a way of overriding specific methods (like the 
> visit
> (MethodDeclaration n, A arg) and not all of them.

I keep running into this problem again and again myself, so I thought
I might post some notes.  The short of it: doing anything fancy with
proxy gets hard fast but there is a way to do it with gen-class.

In my case I'd like to subclass java.io.InputStream and implement the
abstract read() method but NOT override the non-abstract read(byte[]
b) and read(byte[] b, int off, int len).

So what I really want to do is something like this:

(def is
     (proxy [java.io.InputStream] []
       (read []
             (int \a))))

Which works okay for this:

(.read is) => 97

But if anything calls the other overloaded methods like this:

(.read is (make-array (. Byte TYPE) 10))

We get a: java.lang.IllegalArgumentException: Wrong number of args
passed to: user$fn--1844$fn
So it seems as Stuart explained, proxy overrdes all the "read"
methods, not just the zero-arity one.

So my next attempt was to use proxy-super.  This was also the
suggestion from my friendly neighbourhood Clojure guru, Mark Triggs:

(def is
     (proxy [java.io.InputStream] []
       (read [& args]
             (println args)
             (cond (= (count args) 0) (int \a)
                   (= (count args) 1) (proxy-super read
                                                   (first args))
                   (= (count args) 3) (proxy-super read
                                                   (first args)
                                                   (second args)
                                                   (nth args 3))))))

(.read is) => 97
(.read is (make-array (. Byte TYPE) 10)) =>
java.lang.AbstractMethodError: java.io.InputStream.read()I
  0: clojure.proxy.java.io.InputStream.read(Unknown Source)
  1: java.io.InputStream.read(InputStream.java:154)
  2: clojure.proxy.java.io.InputStream.read(Unknown Source)
  3: java.io.InputStream.read(InputStream.java:85)
  4: clojure.proxy.java.io.InputStream.read(Unknown Source)


So what's going on here?  Well, my (.read is bytes) is doing (proxy-
super read bytes) okay. The java code then does a this.read(bytes, 0,
bytes.size).  So my code passes this through to the super-class again
which finally does a this.read().  Unfortunately, as Mark explains
"[proxy-super] works by temporarily hiding your overriding
implementation of a method. For abstract classes that freaks out the
JVM because it looks like you haven't implemented everything." So
since read has been hidden, the java code attempting to call it just
sees it's own abstract method which it obviously can't execute.

In this case I can obviously resort to just reimplementing the other
two methods, but it's a bit frustrating. This is a fairly common
pattern in Java code, needing to subclass something and define one
method foo() where there's a bunch of other overloaded convenience
versions of it in the superclass which call the one you've overriden.

Thankfully, Mark managed to figure out a way of doing it with with gen-
class by using an undocumented (?) feature where you can put types
into the method names so as to pick the exact one you want to
override:

(ns org.example.FancyInputStream
  (:gen-class :extends java.io.InputStream))
(defn -read-void [this]
  (int \a))

This now works as expected. Mark's notes on this are here:

http://dishevelled.net/Tricky-uses-of-Clojure-gen-class-and-AOT-compilation.html

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