3

I am writing Clojure code interacting with external Java library. One of the method returns Java Optional object. Let's assume that it returns Optional<String> and I need to change the string value in the Optional object, for example, to lowercase. I'd use map method if I write the code in Java:

Optional<String> ret = someObj.someMethod();
ret.map(String::toLowerCase)
   .map(...)
   .orElse("x");

So, I need to call someObj.someMethod() in Clojure and have to do the similar work. What I found is this: Passing a Clojure function as java.util.Function

So I wrote code like this:

(defn ^java.util.function.Function jfn [f]
  (reify java.util.function.Function
    (apply [this arg] (f arg))))

(let [ret (.someMethod someObj)]
  (.. ret
      (map (jfn (fn [x] (s/lower-case x))))
      (map (jfn (...)))
      (orElse "x")))

In order to pass clojure function to the place that expects Java lambda, I used jfn defined like the above. And actually it works fine.

But I am not sure this is the best way to do this as I have to wrap clojure function that calls Java method inside with Java's Function.

Are there any better/simpler way to do this? It will be better if we can call Java String's toLowerCase method directly.

3
  • 1
    I'm not sure if it fits your use case, but you could "unbox" the Optional early on, and use some-> macro to do a similar operation. Even if you needed an Optional with the value later, it might be easier to re-box the value later than to do Function interop. Commented Apr 2, 2020 at 12:32
  • @TaylorWood Thanks for your feedback. I think your idea is valid and worth to be an answer. Commented Apr 2, 2020 at 13:11
  • I like your first approach better. If you don't like boxing, and your functions are all literals, you could make jfn a macro instead of a function: (defmacro jfn [[arg] & body] (reify Function (apply [_# ~arg] ~@body))), and then (.map ret (jfn [x] (s/lower-case x))). Commented Apr 2, 2020 at 17:51

1 Answer 1

4

I'm not sure if it fits your use case, but you could "unbox" the Optional early on, and use some-> macro to do similar short-circuit-on-null function composition.

Even if you needed an Optional with the value later, it might be easier to unbox the value early and re-box the value later, rather than Function interop.

(defn optional->nilable [this]
  (when (.isPresent this)
    (.get this)))

(def maybe (Optional/of " something "))

(some-> (optional->nilable maybe)
        (clojure.string/trim)
        (not-empty)
        (clojure.string/upper-case))
; => "SOMETHING"

Then if you needed to convert back to Optional:

(Optional/ofNullable *1)
Sign up to request clarification or add additional context in comments.

1 Comment

You could also unwrap inline, (some-> (.orElse maybe nil) ,,,)

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.