Share your thoughts in the 2024 State of Clojure Survey!

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

+3 votes
in Java Interop by

Problem:
- Various Java APIs depend on extension of abstract base classes rather than interfaces
- "proxy" has limitations (no access to protected fields or super)
- "proxy" has performance overhead because of an extra layer of functions / parameter boxing etc.
- "gen-class" is complex and is complected with compilation / bytecode generation

In summary: Clojure does not currently have a good / convenient way to extend a Java abstract base class dynamically.

The proposal is to create a variant of "reify" that allows the extension of a single abstract base class (optionally also with interfaces/protocols). Code generation would occur as if the abstract base class had been directly extended in Java (i.e. with full access to protected members and with fully type-hinted fields).

Since this is a JVM-only construct, it should not affect the portable extension methods in Clojure (deftype etc.). We propose that it is placed in an separate namespace that could become the home for other JVM-specific interop functionality, e.g. "clojure.java.interop"

Proposed solution: The attached patch proposes an implementation for this feature, providing a new clojure.interop namespace, containing the defclass and extend-class macros.

`
"(defclass name [fields*] options super-class super-args specs)

Like clojure.core/deftype but can extend a concrete class, override
and invoke public and protected methods defined in the super class or
one of its base classes and access/set! public and protected fields of
those.

super-args is a (possibly empty) vector of arguments to the superclass
constructor

It is possible to invoke a super method while overriding it, by type-hinting
the parameter this as the super class: (. ^SuperClass this method args*)"
`

`"(extend-class options super-class super-args specs)

Like clojure.core/reify but can extend a concrete class, override
and invoke public and protected methods defined in the super class or
one of its base classes and access/set! public and protected fields of
those.

super-args is a (possibly empty) vector of arguments to the superclass
constructor

It is possible to invoke a super method while overriding it, by type-hinting
the parameter this as the super class: (. ^SuperClass this method args*)"`

Patch: 0001-CLJ-1225-add-defclass-extend-class-v2.patch

5 Answers

+1 vote
by

Comment made by: bronsa

Attached a proposed impl for this feature

+1 vote
by

Comment made by: bronsa

Updated patch is the same as v1 but refreshed on top of master

+1 vote
by
Reference: https://clojure.atlassian.net/browse/CLJ-1255 (reported by mikera)
by
I'm working on code that could really benefit from this too.
by
We are interested in this idea and we have taken a fly-by of this ticket several times, but the approach in the patch here that modifies the compiler is more invasive than we want. It's been a while since I've had this conversation, so I don't remember much of the details beyond that.
0 votes
by

Comment made by: alexmiller

From Rich: we do not want to support abstract classes in a portable construct (reify, deftype). However, this would be considered as a new Java-only construct (extend-class or reify-class). If you could modify the ticket appropriately, will move back to Triaged.

0 votes
by

Comment made by: bronsa

More documentation about the proposed impmlementation can be found at https://docs.google.com/document/d/1OcewjSpxmeFRQ3TizcaRRwlV34T8wl4wVED138FHFFE, non squashed commits at https://github.com/clojure/clojure/compare/master...Bronsa:defclass

...