Generating reflection-free Java wrappers

Nice! I never had to deal much with the Bean stuff (except for a project in uni involving enterprise java beans, still gives me the shivers), but this looks really useful.

Many Java APIs are replete with them, if only to model data with. This library was motivated by using the AWS SDK from Clojure, for example. (Today I just use Cognitect’s AWS lib for this case.)

2 Likes

I see. Ya, actually that’s pretty neat, I think you’re on to something.

Would be nice if you could also have it so the macro throws when there is ambiguity, and allows you to explicitly type hint them. That would achieve maximal performance.

Or we should benchmark the different approach. Cause it is true the JIT might be able to optimize the instance of away.

I like the idea of just lifting functions into a namespace (inverting the OOP stuff basically).
I did something like this out of necessity, but for a narrower use-case: selectively exposing private fields with accessor functions that had no business being private (from jfreechart I think, maybe piccolo2d). I came off of that thinking "why isn’t this the default for interop? just have a library of functions automatically generated. Then you went and did it, hah!

1 Like

This is great, data hiding can be so overrated :slight_smile:

Does this still work on Java 9+?

This seems pretty cool and exactly what I need at the moment!!! :smile:

Nice! Do let me know what you think once you’ve managed to use it a bit!

I haven’t tested it, but it’s definitely on the way out due to the breaking changes they introduced (you know…not allowing you to be a consenting adult in favor of modularity - silliness IMO).

It should work (with warnings and/or jvm startup flags) for the time being, but at some point using reflection to crack open object vaults will be verboten. Thankfully, I’m likely to still be on java 1.8 by then due to the nature of my work :wink:

This also reminds me of an old lib I wrote to get better transparency of Java objects: gavagai. It creates objects that expose a lazy map-like interface for java beans and bean-like objects in a recursive way. You can customize precisely what you want to expose and how you optionally want to transform values. It emits functions that are annotated to do no reflection on access.

By the way, it has had no commit in over 4 years, and still works perfectly with clojure 1.10.1 and Java 12, kudos for stability!

1 Like

This would be a great foundation for implementing datafy/nav for all kinds of objects.

Hi,

this looks pretty interesting. I have done a similar-ish thing for java.time, the output of which is in this lib: https://github.com/henryw374/cljc.java-time. It’s a bit different in that it is .cljc and has to deal with a few irregularities in the underlying js impl of java.time.

I also found type hinting a bit of a struggle and so quite a bit is still tbd… I might leverage some of your code to help with that :-).

Btw I had a quick go with e.g. java.time.ZoneId and see the overloaded ‘of’ method is missing the (String,bool) version.

@henry_w where are you seeing this ZoneId/of method, I can only find (String) and (String,Map) versions.

https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/time/ZoneId.html

ah sorry, it has default visibility, not public. so not expected

Hey, so I have taken bits of this to generate better type hinting in my java.time lib - thanks again.

One case that is still problematic is java.time.Year#isLeap. There’s a static and non-static versions with the same name - urghh. it remains special-cased for now.

btw I’ve been referring to the java.time lib as a ‘vacuum wrapper’, as in this kind of thing https://images.app.goo.gl/x8ctNQBPYewXTLy3A - in case you’re looking for names/analogies.

This is really cool! I’ve been thinking of doing something similar for a while (not necessarily the reflection-free part, although that is definitely a bonus, but automatically generating idiomatic Clojure wrappers around Java APIs) but hadn’t got around to it yet.
I’ve been trying it out, and have started making some changes to get it working for me:

  • I’ve fixed a bug with way arguments are counted: it wasn’t consistent between static/non-static methods, so would fail if you have a class with e.g. public void foo(int a) & public static void foo(int a, int b)
  • I’ve started looking into adding support for vararg methods, as I will need that

I hope you don’t mind that I’ve created a github repo (forked from your gist, so it’s clear what code is yours) to develop it further, here. As you’ve done most of the hard work setting up the foundations, it would be great to be able to make use of that, but I don’t want to be seen as trying to take credit for your work - so let me know if you want me to take it down, or make any changes to clarify the status.

5 Likes

This is great! Thanks for making a proper project out of it. I have enough projects to manage already so you are welcome to run with this one.

1 Like

Thanks! I’ll see how far I get with it.
I’m currently trying to work out the best way to support vararg methods, which is turning out to be a bit tricky, with lots of edge cases… but hopefully I’ll get something working soon.
Then I want to use it to generate a Clojure wrapper around Apache Spark, which should start exercising it pretty well, and probably throw up lots more issues to smooth out.

Hi @plexus,
I’ve been making some progress with this, and have vararg methods pretty much working now, but I have a quick question: Is there a reason that you let-bind the type-hinted arguments and then call the java method with them, as opposed to just type hinting in the method call?
I.e., when you do something like:

(let [this ^SomeClass this
      a (long a)]
  (.method this a))

could that be replaced with:

(.method ^SomeClass this (long a))

I think that would simplify the macro logic somewhat, but don’t want to break something subtle if that extra level of let binding is actually needed.

I can’t immediately remember if there was a specific reason why I did it this way. At first glance either approach should work.

OK thanks, I’m going to try simplifying it. Hopefully with *warn-on-reflection* set, I should notice if it breaks anything.

1 Like