(defmacro crack-abstract
  "implements the abstract methods of a class by forwarding calls to a generated interface."
  [klass-sym method-prefix interface-name additional-methods factory-name]
  (let [klass (resolve klass-sym)
        methods (->>  klass
                     (.getMethods))
        method-names (->> methods
                          (filter #(java.lang.reflect.Modifier/isAbstract (.getModifiers %)))
                          (map #(.getName %))
                          (into (set (map name additional-methods))))
        required-impls (for [method methods
                             :when (contains? method-names (.getName method))]
                         method)
        impl (with-meta (gensym 'impl) {:tag interface-name})
        ctor-argcs (->> klass
                        (.getDeclaredConstructors)
                        (map #(.getParameterCount %))
                        (distinct)
                        (sort))]
    `(do
       (definterface ~interface-name
         ~@(doall
             (map first
                  (vals
                   (group-by first
                             (for [method required-impls
                                   :let [argc (.getParameterCount method)
                                         params (take argc (repeatedly gensym))]]
                               `(~(symbol (str (name method-prefix) (.getName method) (.getParameterCount method)))
                                 ~(vec params))))))))
       (defn ~factory-name
         ~@(doall
            (for [ctor-argc ctor-argcs
                  :let [params (take ctor-argc (repeatedly gensym))]]
              `([~@(cons impl params)]
                (proxy [~klass-sym clojure.lang.IDeref] [~@params]
                  (deref [] [~impl ~@params])
                  ~@(doall
                      (for [[method-name methods] (group-by #(.getName %) required-impls)]
                        `(~(symbol method-name)
                          ~@(doall
                             (for [argc (distinct (map #(.getParameterCount %) methods))
                                   :let [params (take argc (repeatedly gensym))
                                         forward-method-name (symbol (str (name method-prefix) method-name argc))]]
                               `(~(vec params)
                                 (. ~impl (~forward-method-name ~@params))))))))))))))))

Generated At 2025-03-01T17:35:54-0800 original