How to call a reified Java interface from a class in Clojure? Call can't be resolved

312 views Asked by At

I am trying to translate some Java code directly into Clojure on a raspberry pi. I am stuck at implementing an interface in a method call - addListener.

I have tried using reify, proxy, and deftype. With reify I have tried providing as many hints to the compiler as possible.

This is the original Java code:

myButton.addListener(new GpioPinListenerDigital() {
  @Override
  public void handleGpioPinDigitalStateChangeEvent(GpioPinDigitalStateChangeEvent event) {
  System.out.println(" --> GPIO PIN STATE CHANGE: " + event.getPin() + " = " + event.getState());
 }
});

And this is my translated Clojure code:

(.addListener myButton
              (reify GpioPinListenerDigital
                (^void handleGpioPinDigitalStateChangeEvent [this ^GpioPinDigitalStateChangeEvent event]
                 (println (str " --> GPIO PIN STATE CHANGE: " (.getPin event) " = " (.getState event))))))

I always end up with the same error:

IllegalArgumentException No matching method found: addListener for class com.pi4j.io.gpio.impl.GpioPinImpl clojure.lang.Reflector.invokeMatchingMethod (Reflector.java:79)

1

There are 1 answers

2
Shlomi On BEST ANSWER

I am not familiar with writing Java for raspberry pi, but looking at the javadoc we see the following declarations:

public void addListener(GpioPinListener... listener);
public void addListener(List<? extends GpioPinListener> listeners);

Both are accepting a multitude of listeners, not just one. In the Java example you provided above, java compiler turns the single listener instance into a singelton vector transparently, and uses the first definition shown above.

The clojure compiler does not know how to do that, you have to help it. The question essentially boils down to how to call variadic java functions from clojure.

Without testing this, I believe the solution will be using clojure's into-array function, so something like the following:

(.addListener myButton
              (into-array GpioPinListenerDigital
                [(reify GpioPinListenerDigital
                  (^void handleGpioPinDigitalStateChangeEvent [this ^GpioPinDigitalStateChangeEvent event]
                   (println (str " --> GPIO PIN STATE CHANGE: " (.getPin event) " = " (.getState event)))))]))

You may have to twist this a little bit, but I believe that is the core problem you are facing.

Edit

Due to the second declaration above, another potential solution may simply be wrapping it in some regular list, such as a vector:

(.addListener myButton
              [(reify GpioPinListenerDigital
                (^void handleGpioPinDigitalStateChangeEvent [this ^GpioPinDigitalStateChangeEvent event]
                 (println (str " --> GPIO PIN STATE CHANGE: " (.getPin event) " = " (.getState event)))))])