Next up in the series complaining about Clojure’s Java interop is proxy. While vararg method calls are inconvenient at worst, there are some (I’d say common) things that simply cannot be achieved with proxy.
Once again this is something I ran into while working with Netty. In one of the HTTP/2 examples, they have one implementation extending AbstractHttp2ConnectionHandlerBuilder<T, B> (have I mentioned I find these extremely long Java class names just hilarious?). The Java implementation is pretty straightforward: implement the abstract method of the class and be done with it.
This is something that is (as far as I know) impossible using proxy. Not the extending/overriding part, that works okay. The problem here is that the (protected) methods defined in the extended abstract class rely on the implemented functionality of the abstract class. The call chain goes something like this (arities are important): the extending class’s
public build() calls the abstract class’s
protected build(), which then calls
buildFromConnection, which calls
buildFromCodec, which then calls the abstract
build(decoder, encoder, settings) the extending class defines.
The problem is that Clojure’s proxy is a “surface” thing. It doesn’t cleanly
@Override but works by some other magicks. Which means that even if you define a 0-ary and a 3-ary
build method in your proxy, the 3-ary will never actually get called from within the abstract parent class’s scope. Proxied methods are as good as non-existent from the point of view of the extended Java class.
So if you’re in the unlucky position that you have to extend an abstract class that would call your implemented methods, you’ll have to write it in Java (or some other JVM language that can handle this, like Kotlin) because Clojure’s proxy is incapable of that.