5

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

2 Answers 2

7

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.

Sign up to request clarification or add additional context in comments.

2 Comments

Ah, that did the trick, thanks! including the this reference eliminated the compiler error and the following code at the REPL works as expected: (.execute (action (fn [metadata parameter] (println "Test action"))) nil nil)
@OliverMooney, glad to help!
2

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.

1 Comment

I didn't realise that, thanks! I've mainly used IntelliJ as a host for Cursive, most of the Java-oriented menu options are unknowns so far...

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.