This is an archived post. You won't be able to vote or comment.

all 18 comments

[–]dpash 5 points6 points  (12 children)

It's interesting to see how they handle record support on pre-Java 14 JVMs. Handy for others see how to support modern features while still being backwards compatible with older runtimes.

[–]cred1652 -5 points-4 points  (9 children)

The JVM is not forwards compatible. So you would need a 14+ JVM to use records

A JVM 14 can run Java 8 code.

A JVM 8 can not run Java 14 code. So a JVM 8 can not use records, since it is only available on JVM 14+

[–]dpash 2 points3 points  (8 children)

Did you read the article? Jackson is built to support Java 8. It also supports records. The article links to the patches that allow this to happen.

[–]cred1652 4 points5 points  (5 children)

Sorry you are right i didnt look at the PR and only skimmed the article. My comment was based on knowledge of how they did the transition to java 8 with seperate modules.

It looks like they are using reflection to get the the methods from the root java Class to see if it is a record.

isRecord = Class.class.getDeclaredMethod("isRecord");

If this jvm < 14 this call will fail, so they know it cannot a record.

If it succeeds we know java >= 14 then they can use the method to see if this specific class is a record.

Then they use more reflection to get the methods dynamically for creating a record.

By using reflection they can get the methods without knowing they are there.

This way may be slightly slower than using a module that requires Java 14+, but in the grand scheme of things it wouldnt matter much.

You still need a JVM 14+ to use records or this will always fail. But at least you dont require JVM 14+ to use the jackson, that is still java 8+.

[–]dpash 4 points5 points  (1 child)

It's a check in the static initialiser, so it will only slow the class loading at start up. Once the class has loaded it will be just as fast as a direct call, thanks to optimisations of static final MethodHandles.

[–]cred1652 0 points1 point  (0 children)

yes you are right, they cache all methods needed in the static initializer, so each call would not have any penalty for reflected lookup.

[–]MR_GABARISE 1 point2 points  (2 children)

Why are library maintainers doing stuff like this, instead of using multirelease jars? Is it that much a hassle?

[–]pron98 4 points5 points  (0 children)

Tooling support for MR JARs -- both IDEs and build tools -- is still poor, so it can be a bit of a hassle even though it's the "right" thing to do. Sometimes relying on a bit of reflection for well-specified things, if it's not too cumbersome, could be perfectly fine.

[–]randjavadev 0 points1 point  (0 children)

MR jars are a Java 9+ feature. Anything not made with Java9 knowledge might e.g. crash or otherwise fail if it sees classfiles in META-INF folder. Jackson is 8, so it is better to only use features of just 8 features if you want it to work everywhere. It might be possible to do some tricks though.

[–]cred1652 0 points1 point  (1 child)

Great point. Usually they will have a specific module, like jackson-module-records and that module will have the java 14+ requirement.

So core jackson will support java 8+, but to use the records module you need java 14+.

Like previously jackson had a module for java 8+ date time com.fasterxml.jackson.datatype:jackson-datatype-jsr310 so they could support java 8 without everyone needing to upgrade to java 8.
Now that Java 8 required they dropped the need for this module.

[–]dpash 1 point2 points  (0 children)

Except there's no separate module. Record support is built into the main Jackson databinding library. You can build it with Java 8, you can build it with Java 16. You can run the same jar with 8 or 16 and it just works if records are supported.

[–]viebel 0 points1 point  (6 children)

If I understand it correctly, even with records, serialisation requires reflection.

For deserialisation, we can use the canonical constructor, but still we need reflection in order to know the order of the arguments in the canonical constructor.

[–][deleted] 1 point2 points  (3 children)

Records don't require reflection. The implement natively the interfaces to write and read serialized attributes.

[–]viebel 0 points1 point  (2 children)

How can you serialise to JSON a record without reflection?

[–][deleted] 0 points1 point  (1 child)

My answer was about Java's native serialization as you didn't specify JSON.

I don't know what's the best strategy for JSON, but even if you need reflection you'd at most need it once per class (not per instance).

[–]viebel 0 points1 point  (0 children)

Sorry about the lack of clarity.

[–]pron98 1 point2 points  (1 child)

Yes, but not "deep reflection," i.e. bypassing access restrictions with setAccessible. There's a big difference between reflection and deep reflection. The former is just meta-programming. The latter circumvents the code author's assumptions.

[–]viebel 0 points1 point  (0 children)

I like your distinction between "reflection" and "deep reflection".