Welcome! Please see the About page for a little more info on how this works.

0 votes
in Java Interop by
  (deftype l [^java.util.List x])
  (use '[clojure.reflect :as r])
  (:members (r/reflect (l. `())))

Despite the type-hints of java.util.List, on reflection x seems to always be an Object

Have been specifically trying to get an optaplanner example working in 100% clojure https://github.com/joinr/optaplanner-clj/tree/163d326a6433b88ca9811e33d30758ceff42b91f . The code is evaluating but this error keeps occurring on running (solve (->problem))

  1. Unhandled java.lang.IllegalStateException The solutionClass (class optaplanner_clj.data.TimeTable) has a
    PlanningEntityCollectionProperty annotated member (public final
    java.lang.Object optaplanner_clj.data.TimeTable.lessonList) that does
    not return a Collection or an array.

2 Answers

0 votes

Others besides me can answer more authoritatively here, but I am pretty sure that methods created by the Clojure compiler for Clojure functions, and deftype methods, can have one of the three types java.lang.Object, primitive long, or primitive double for parameter types and return types. Anything other than those, and your most effective bet is using something other than the Clojure compiler to create the JVM byte code, e.g. write some Java wrapper code and compile with javac, or learn some library for generating JVM byte code like the asm library that the Clojure library does.

This is correct. If you want a class with specific types, you need to for example create an interface with the specific types, then have the deftype implement the interface.
The problem here is optaplanner forces doing lots of annotations, and specifically reflects on the class's annotated field during its validation phase.  If the field isn't typed (which deftype is forcing to object), then optaplanner chokes.  Even with an interface with getters and setters supplied.  Reflection happens on the field.  We got the other annotation jank working though.  Aside from some other work around, it looks like fully conforming to optaplanner interop requires custom class definition (e.g. with the insn library), or messing with what deftype emits.
0 votes

For reference, it looks like clojure.tools.emitter.jvm DOES preserve the type of non-primitive fields....

 (require '[clojure.tools.emitter.jvm :as e])

(e/eval '(deftype blah [^String x]) {:debug? true})
// class version 50.0 (50)
// access flags 0x31
public final class user/blah implements clojure/lang/IType  {

  // compiled from: user/blah

  // access flags 0x11
  public final Ljava/lang/String; x

user> (require '[clojure.reflect :as r])
user> (r/reflect (user.blah. "hello"))
{:bases #{java.lang.Object clojure.lang.IType},
 :flags #{:public :final},
 #{{:name getBasis,
    :return-type clojure.lang.IPersistentVector,
    :declaring-class user.blah,
    :parameter-types [],
    :exception-types [],
    :flags #{:public :static}}
   {:name user.blah,
    :declaring-class user.blah,
    :parameter-types [java.lang.Object],
    :exception-types [],
    :flags #{:public}}
   {:name x,
    :type java.lang.String,  ;;in clojure
    :declaring-class user.blah,
    :flags #{:public :final}}}}