Java 15: New and Notable
Tuesday, September 15, 2020 |JDK 15 hit General Availability today. While I spend most of my time in Kotlin these days, I do keep a close on Java, as it still has a special place in my heart, so I thought I’d make a quick post highlighting some of its new features. :) There are quite a few changes in the release, so I’ll list all of them, but focus on the ones I think most developers will find more interesting.
The Complete List
If you look at the project page, you will see a list of all the JEPs (JDK Enhancement Propsals). I’ll save you a click and reproduce that list here:
-
371: Hidden Classes
-
378: Text Blocks
General Notes
There are some pretty techincal changes here (it seems to me) that most people won’t care about. If you’re into crypto, the EdDSA change might be of interest,. If you’re performance-sensitive, ZGC will likely pique your interest, and if you’re a framework developer, hidden classes sound pretty awesome. Solaris, RMI, and Javascript users, though, may be in for a slight disappointment, but these changes were telegraphed long ago, so they shouldn’t come as a surprise.
For my money, the most interesting (read as: applicable to me) are
-
Sealed classes
-
Pattern matching instanceof
-
Text blocks
-
Records
Let’s take a super quick looks.
Sealed Classes
There’s a good chance that at some point in your career, you’ve needed to define a hierarchy of classes and control where it can be extended. In languages like Kotlin, there is the sealed
modifier that prevents the class from being extended without permission, so to speak. In Java however, you’re carefully curated list of system status codes, say, can be extended willy nill by any and all. Until now. the new feature, for those not familiar with the concept, allows the developer to "seal" a class, preventing its extension by all except a special few. In it’s simplest form, it might look like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
abstract sealed class Shape {
abstract public draw();
}
final class Square extends Shape {
public draw() {
}
}
final class Circle extends Shape {
public draw() {
}
}
In this example, Shape
is abstract (though it need not be), and there are only two available shapes: Square
and Circle
. In this example, all three classes are declared in the same file, but, unlike, say, Kotlin, that need not be the case. If your classes are complex, have a lot of code, etc., and it would be better from a maintainability perspective, say, to have them in separate files, the compiler will let you do that, but you have to tell it which
classes are allowed to participate:
1
2
3
4
5
public abstract sealed class Shape
permits fully.qualified.class.Square,
fully.qualified.class.in.another.package.Circle {
...
}
Interfaces can also be sealed, with all of the same rules applying.
Pattern-matching instanceof (Preview feature)
For years, we’ve used instanceof
to test the type of a variable, and for years, immediately after, we’ve type-casted that variable so we can use it:
1
2
3
if (foo instanceof Interface1) {
((Interface1)foo).someMethod();
}
Having to type (har har) twice was a bit annoying. JDK 15 brings a new syntax that lets us reduce that:
1
2
3
4
5
if (foo instanceof Interface1 f) {
f.someMethod();
} else {
f.someOtherMethod(); // compiler error
}
Notice that we can declare a variable after the instanceof
check that we can then use in the true
branch of the if
. The variable is not available in the else
.
This does not currently work in a switch
statement, but that change is expected to come as well. This is only a preview feature so we can kick its tires, so use it with caution.
Text blocks
One of the things I’ve loved about Kotlin is the ability to write multi-line strings and let the compiler do the hardwork for me. For example.
1
2
3
4
val foo = """
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus luctus
finibus velit dictum lacinia.
""".trimIndent()
This allows us to write a text block, spanning lines, and the system does the work for us to make a single, properly formatted string. In Java, that might look like
1
2
String foo = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus luctus\n" +
"finibus velit dictum lacinia.";
JDK 15 allows this:
1
2
3
4
String foo = """
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus luctus
finibus velit dictum lacinia.
""";
Very neat and clean.
Records
Finally, we come to records.In a nutshell, Records, a new kind of class on the JVM, allow us to declare an new, immutable type primarily intended to carry data in a _very_concise manner. Take this example from the JEP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// The old way
class Point {
private final int x;
private final int y;
Point(int x, int y) {
this.x = x;
this.y = y;
}
int x() { return x; }
int y() { return y; }
public boolean equals(Object o) {
if (!(o instanceof Point)) return false;
Point other = (Point) o;
return other.x == x && other.y = y;
}
public int hashCode() {
return Objects.hash(x, y);
}
public String toString() {
return String.format("Point[x=%d, y=%d]", x, y);
}
}
// The new way
record Point(int x, int y) { }
A huge improvement. The eagle-eyed may notice that the record
definition is missing some methods. That is because, for record
classes, the compiler generates equals
, hashCode
, and toString
for you. It also generates getters and setters as well. While the JEP officially states that this isn’t the opening salvo of a War on Boilerplate, it’s at least a nice test shot. :P