0
votes

From my research, the way to extend a Java class is basically with either gen-class in a namespace or with proxy. But looking at the Clojure type selection flowchart, it seems to suggest that I can use record to extend a Java class:

  1. Will the type need to extend a Java class or implement any interfaces? Yes
  2. Do you need a named type or only an instance of an anonymous type? Named type
  3. Do you need to be able to refer to the class statically from Java? No
  4. Is your class modelling a domain value - thus benefiting from hasmap-like functionality and sematics? Yes

Use defrecord

So the question is... how?

For example (from this):

public final class Second extends Struct {
    public final Signed32 a_number = new Signed32();
    public Second(final Runtime runtime) {
        super(runtime);
    }
}

public final class Top extends Struct {              
    public final Second second = inner(new Second(getRuntime()));  
    public final Second[] seconds = array(new Second[5]);
    public final Signed32 another_number = new Signed32();
    public final Signed32[] more_numbers = array(new Signed32[5]);
    public Top(final Runtime runtime) {
        super(runtime);
    }
}

Do I...

(defrecord Second)
(extend jnr.ffi.Struct
        Second)

?

2

2 Answers

0
votes

I think the flow chart is wrong. Checking the Clojure - Datatypes: deftype, defrecord and reify documentation we can see that:

  • a deftype/defrecord can implement one or more protocols and/or interfaces

There is no mention of extending an existing class. Indeed, looking at the documentation for defrecord itself there is no mention of extending existing classes, except for Object as a special case.

Sorry, it looks like either proxy or gen-class are your only options.

0
votes

Why do you need a named type? If, as you say, you don't need to refer to it statically from Java, I don't see any particular reason to name the type. And if you answer that question correctly, it leads you to proxy. Your problem is you've chosen inconsistent answers: you say you need a named class, but don't answer yes to any of the reasons you might need one. The reason the flowchart has in mind is that Java code may wish to refer to your code by name, in which case gen-class. But it makes little sense to say on the one hand that you must extend a Java class, and on the other that your new type will be a simple data carrier, modeling a domain value. Clojure records are great at that, but having a yucky mutable base class from Java doesn't make for a good domain object.

That said, if you'd answered "no" to "modeling a domain type", it would lead you to deftype, which also cannot extend a class. I think it's a mistake that it ever allows you to escape the "interop zone" if you want to extend a class: it would be better to keep interfaces and subclassing separate, but a flowchart can only be so large.

FWIW the comments on the original flowchart post include your question, with an answer by the author, echoing my suggestion to use proxy and the issue that the flowchart is big enough already:

That first decision isn’t exclusively indicating that you need to extend a class — it includes implementing interfaces. So, there is a bit of ambiguity there. Of course, you’re in gen-class-land there, but breaking things out more to eliminate the ambiguity would make the flowchart a fair bit bigger, I think.

FWIW, I’d personally drop the named type requirement there, use proxy, and just def the result of (class (create-proxy-instance …)) for instance? checks and such (assuming that that’s why you need the named type).