Skip to content

Avoiding Reflection

Justin Conklin edited this page Sep 28, 2017 · 1 revision

When using insn.core/new-instance, how do we avoid reflection when using instances of our types? One anwser is to implement interfaces (or protocols) defined in Clojure. (We can, of course, also implement interfaces defined in Java.)

For example, consider the following type that defines a simple math operation:

(def math-type
  {:methods [{:name :inc, :desc [:int :int]
              :emit [[:iload 1]
                     [:ldc 1]
                     [:iadd]
                     [:ireturn]]}]})

If we use an instance of our type now, Clojure will have no choice but to reflect upon our object to determine if the method call is valid:

(require '[insn.core :as insn])
(set! *warn-on-reflection* true)

(-> math-type insn/new-instance (.inc 42)) ;; => 43
;; Reflection warning ... call to method inc can't be resolved (target class is unknown).

As previously stated, the answer is to define an interface or protocol from Clojure and have our type implement it. Note that we need to implement a boxed version of our operation to satisfy the protocol. We'll cheat and just use Clojure's built-in Numbers/inc:

(definterface IMath
  (^int inc [^int n]))

(defprotocol PMath
  (-inc [x n]))

(def math-type2
  (-> math-type
      (assoc :interfaces [IMath (:on-interface PMath)])
      (update :methods conj {:name "_inc", :desc [Object Object]
                             :emit [[:aload 1]
                                    [:invokestatic clojure.lang.Numbers "inc" [Object Number]]
                                    [:areturn]]})))

If the :on-interface lookup is unfamiliar to you, it holds the underlying interface that the protocol uses. The -inc protocol fn calls said interface's _inc method internally, which is what we implement.

Now we can use our type without reflection via a type hint:

(-> math-type2 ^IMath (insn/new-instance) (.inc 42)) ;; => 43

We can also use our type with the PMath protocol with no type hint at all:

(-> math-type2 insn/new-instance (-inc 42)) ;; => 43