Clojure gen-class for overloaded and overridden methods
Asked Answered
B

1

7

I'm trying to use gen-class to override the compare(WriteableComparable a, WriteableComparable b) method in this class in clojure. The complication comes from the fact that this method is overloaded 3 times:

  • int compare(WritableComparable a, WritableComparable b)
  • int compare(Object a, Object b)
  • int compare(byte[] b1, int s1, int l1, byte[] b2, int s2, int l2)

So far my attempt looks like this:

(gen-class
 :name comparators.MyWriteableComparator
 :extends org.apache.hadoop.io.WritableComparator
 :exposes-methods {compare superCompare}
 :prefix "interop-")

(defn interop-compare
  ([this a b c d e f]
     (.superCompare this a b c d e f))
  ([this ^WritableComparable w1 ^WritableComparable w2]         
     (.compareTo (.getSymbol ^SymbolPair w1) 
                 (.getSymbol ^SymbolPair w2))))

Everything compiles, but when I run it, I'm getting a null pointer exception, and I suspect that it is because I overrode the wrong method (i.e. the compare(Object a, Object b) instead of the intended compare(WritableComparable a, WritableComparable b)). For reference, the Object version of compare calls through to the WriteableComparable version.

It's totally possible that the NPE is coming from something else, but I've at least narrowed it down to this clojure code (when I run it with the corresponding Java version, things work well).

Is there a way to specify which overloaded version of the method should be used?

(I tried adding a :methods clause into the gen-class call, but I learned that one should declare only new methods, not superclass methods.)

Blaze answered 25/9, 2015 at 2:29 Comment(0)
D
9

There is a mechanism that works with gen-class and allows overriding same arity overloaded methods. We can define vars / functions with names that include the types of the arguments in addition to the prefix and the method name. To override a method like foo(String s, Object o) we can define a var named -foo-String-Object. The code will look for a var thus named before falling back to -foo. This is documented at least in one of the Clojure mailing list threads.

In practice, this means that you can write code like:

(defn interop-compare [this a b c d e f]
  (do-array-compare))

(defn interop-compare-Object-Object [this a b]
  (do-object-compare))

(defn interop-compare-WritableComparable-WritableComparable [this a b]
  (do-writable-comparable-thing))
Disastrous answered 25/9, 2015 at 22:20 Comment(2)
Wow. One more way in which use of gen-class is a dark art. I had wondered about this question, but didn't need an answer, so put it out of my mind.Sharolynsharon
For my future reference, you need to add to :methods duplicate entry for interop-compare, one of input Object Object and one of input WritableComparable-WritableComparable for this to work.Somerville

© 2022 - 2024 — McMap. All rights reserved.