I wasn’t here a week ago (I had a speech at JUG and time-wise I didn’t make it anymore), but that doesn’t mean that somehow a lot has happened during my absence. Nevertheless, I have two JEPs for you today, as well as some exceptionally interesting new releases.
1. JEP 401: Implicitly Value Object Creation (Preview)
Project Valhalla strikes again and materializes another set of changes in the form of JEP 401: Implicitly Value Object Creation (Preview) This is interesting, since the whole proposal was until recently called Null-Restricted Value Object Storage and even before that Primitive Classes.
However, getting to the point, this JEP aims to improve the performance of storing Value classes – ones designed to have a similar performance profile to primitive types, but with additional benefits (such as the ability to create abstractions and encapsulation) provided by fact, they are classes. This is quite an oversimplification, but Valhalla probably was the basis for several PhDs, so it’s better this way.
Value classes are designed to be faster and use less memory than regular classes. However, they are limited in use because they do not allow null values and are not atomic, meaning they cannot be modified by multiple threads simultaneously.
To address these limitations, JEP introduces two new features: optional constructors and null-restricted types, the introduction of which was discussed at one time on mailing lists. Optional constructors allow programmers to create instances of value classes in a different way than is the case with other classes – it is possible, for example, to omit the constructor. On the other hand, null-restricted types allow programmers to declare that a variable or a value returned by a method cannot have a null value and automatically initialize variables with a default instance of the class when they are created (that’s why optional constructors are needed).
It also provides additional syntax. To show it, I’ll show an excerpt from the original JEP. If you are curious to see what the additional null handling syntax will look like, we are talking about such:
interface Foo<T> {
T* get(); // Foo<char> returns char
T! getNonNull(); // Foo<char> returns char
T? getOrNull(); // Foo<char> returns Character?
T getOrAlternate(Supplier<T> alt); Foo<char> returns Character
}
Of course, a lot more is probably happening beneath the surface, as we’re only talking about the Draft here, so things can change. To emphasize how early a version we are dealing with – JEP 401 links to Nullness Marker JEP which…. does not yet exist. At the time of writing, the link returns a 404 response.
Zainstaluj teraz i czytaj tylko dobre teksty!
2. JEP 443: Unnamed Patterns and Variables
After a rather complicated, very early-stage JEP on Valhalla, we will look at the somewhat simpler JEP 443: Unnamed Patterns and Variables (Preview), which recently reached candidate status.
Let’s start with unnamed patterns. Consider the following code:
record Point(int x, int y) {}
record ColoredPoint(Point point, String color) {}
Let’s assume that we want to extract the x-coordinate of an instance of ColoredPoint, but we don’t care about its color. We can do this using an unnamed pattern:
ColoredPoint coloredPoint = new ColoredPoint(new Point(3, 4), "red");
if (coloredPoint instanceof ColoredPoint(Point(int x, _), _)) {
System.out.println("x-coordinate is " + x);
}
In this example, the „Unnamed Pattern” is represented by the underscore symbol _
in the color
parameter position of the pattern. This allows us to extract the x
coordinate of the Point
component without having to specify the name or type of the color
field.
We already know what Unnamed Patterns are, let’s move on to what Unnamed Variables refer to.
Consider the following code, which enqueus data from a queue and creates instances of Point2D
from a list of coordinates in 3D space.
Queue<Integer> queue = new LinkedList<>(List.of(1, 2, 3, 4, 5, 6));
List<Point2D> points = new ArrayList<>();
while (queue.size() >= 2) {
int x = queue.remove();
int y = queue.remove();
int z = queue.remove();
points.add(new Point2D(x, y));
}
As you can see, in each pass of the loop we drop the value of z
. In this case, we can declare this variable as unnamed using the _
symbol (a bit like in Scala):
Queue<Integer> queue = new LinkedList<>(List.of(1, 2, 3, 4, 5, 6));
List<Point2D> points = new ArrayList<>();
while (queue.size() >= 2) {
var x = queue.remove();
int y = queue.remove();
var _ = queue.remove();
points.add(new Point2D(x, y));
}
This allows us to focus on the variable x
and y
, which are the only ones that are valid in this context. The _
symbol is used as a placeholder for an unused variable and cannot be used anywhere else in the code because it has no name.
Honestly, the above example doesn’t convince me either, but the authors claim that unnamed variables can be useful in situations where variable names are irrelevant or when variables are not used and can improve code readability and maintainability. Additionally, unnamed variables can help reduce false alarms generated by static analysis tools that draw attention to unused variables. Personally, I see the biggest potential for unnamed variables in the case of exception parameters, although the following example also seems to be a code smell:
try {
...
} catch (NumberFormatException _) {
System.out.println("Bad number");
}
3. Release Radar: Dropwizard 3.0 i 4.0
I remember the days when I used Dropwizard, when this promising framework was at the height of its popularity. I thought it was the future of web application development and would remain in the minds of developers for a long time. Unfortunately, life and the market turned out differently. Despite the initial enthusiasm, Dropwizard started to lose its importance, and other tools and technologies overshadowed its glory. Today, as we observe the simultaneous release of versions 3.0 and 4.0, this event goes unnoticed in the world of technology, which only shows how much the perception of this once promising project has changed.
What really catches your attention is the joint release of versions 3 and 4, which can be a bit confusing. The reason for the two versions is simple: Dropwizard 3.0.0 is based on Java EE and the javax.
namespace, so migrating from Dropwizard 2.x to version 3.0.0 should be minimal for many projects. Dropwizard 4.0.0, on the other hand, relies on Jakarta EE dependencies and the jakarta.
namespace, which may involve more work to migrate from Dropwizard 2.x to version 4.0.0 due to more package changes and more significant dependency changes.
The two versions also share some common changes – raising the required Java version to 11, introducing a package structure based on JPMS (I have a feeling it’s one of the first tools taking this standard seriously), updating Jetty to 10.0.x (which also requires a minimum of Java 11), updating Apache HttpClient to 5.x, and removing support for JUnit 4.x (moved to dropwizard-testing-junit4
). In addition, Dropwizard 4.0 will get support for Hibernate 6.0, requiring a move to jakarta.
.
The community’s reaction to Dropwizard 3.0 and 4.0 is quite mixed. Some programmers welcome the new releases with perhaps not excitement, but at least some nostalgia (I myself belong to this group). Ardent fans of Dropwizard talk about its „virtually bug-free” nature and use of proven dependencies. It’s like an old, cozy sweater that you don’t feel the need to throw away. They also appreciate that Dropwizard is not a control freak, unlike some other frameworks (ahem, Spring Boot), allowing programmers to mix and match components as they please.
However, there are also those who believe that Dropwizard is starting to show its age. They argue that more elegant and shiny frameworks have entered the scene, such as Micronaut, Quarkus, and Helidon, which offer modern features such as reactive programming, better performance, and being cloud-native. I don’t think the new version (or rather versions) will be able to change this situation much.
Zainstaluj teraz i czytaj tylko dobre teksty!
4. Release Radar: Hibernate 6.20
And finally, the new Hibernate ORM 6.2.
The release introduces better support for structured SQL types such as Struct, XML, and JSON, allowing users to work with more complex data structures. Related to this feature is the ability to user Records, which enables Hibernate to deserialize the aforementioned structural types in both directions.
The release also unifies the annotations used to generate values. What’s more, Hibernate now offers full support for database partitions using the @PartitionKey
annotation, which simplifies the partition column mapping process.
The last noteworthy feature of Hibernate ORM 6.2.0 is the implementation of SQL MERGE. This mechanism works in a similar way to UPSERT, but it is much more powerful as it allows not only updating but also deleting records, as well as providing additional conditions for the query, such as:
ON CONFLICT ON CONSTRAINT countries_pkey DO NOTHING;
ON CONFLICT (country) DO NOTHING;
Although Hibernate uses it in a way that is invisible to the end user, this novelty has allowed me to expand my knowledge of SQL a bit, so I’m sharing it.
A, and there was also Kotlin 1.8.20, but since KotlinConf is coming up next week, we will cover this topic in the next edition, probably heavily focused on that particular language.