Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Errors extending a Java Interface in Clojure

Tags:

clojure

I have a basic Java interface defined as follows:

public interface Action {
    void execute(Metadata var1, Parameter var2);
}

I'm trying to extend it in Clojure but keep getting errors. After importing the class into my namespace, I've tried using reify as follows:

(defn action [action-fn]
  (reify Action
    (execute [metadata parameter] (action-fn metadata parameter))))

but that throws a compiler illegal argument exception:

CompilerException java.lang.IllegalArgumentException: Can't define method not in interfaces: execute

Next I tried using proxy

(defn action [action-fn]
  (proxy [Action] []
    (execute [metadata parameter] (action-fn metadata parameter))))

That compiles successfully, and my editor (IntelliJ + Cursive) navigates to the interface definition via a border decoration, but trying to invoke execute on a generate proxy fails:

(.execute (action (fn [_ _] "Test action")))

throws the following:

IllegalArgumentException No matching field found: execute for class

Finally I tried using deftype as follows:

(deftype cljAction [action-fn]
  Action
  (execute [metadata parameter] (action-fn metadata parameter)))

which throws the same compiler error as for reify, e.g:

CompilerException java.lang.IllegalArgumentException: Can't define method not in interfaces: execute

Trawling through various blog posts and SO answers seems to suggest it's a problem with the arity of arguments, but I'm not sure how to resolve it. What am I doing wrong??

like image 474
Oliver Mooney Avatar asked Jan 20 '16 14:01

Oliver Mooney


2 Answers

You are missing the this reference from the function. So what you want is this:

(defn action [action-fn]
  (reify Action
    (execute [this metadata parameter] (action-fn metadata parameter))))

Obviously because you are not using it you can just call it _ or whatever makes the most sense in your opinion. When you are calling the function you want this:

(.execute (action action-fn) metadata parameter)

This differs slightly from when you are implementing a protocol. See https://clojuredocs.org/clojure.core/definterface for more information.

like image 171
ponzao Avatar answered Sep 30 '22 13:09

ponzao


ponzao's answer is correct. But note that Cursive can actually fill in the stubs for you: you can write (reify Action) and then (with the cursor in that form somewhere) choose Code->Generate... and choose to implement methods. Cursive will then fill in the stubs with the correct form. This currently only works when implementing interfaces, not protocols.

like image 45
Colin Avatar answered Sep 30 '22 12:09

Colin