Java 15: JEP's that came along…
| Reading Time : 13 minutes | #backenddevelopment #Java
Java 15 which came into General availability brought a very good set of 14 features. Out of those 14 features, few are addition to and few are removed/disabled from current java eco-system. Here are the list of features added
In this blog we will go through a little bit about what these new features are, how they can be used using few code snippets.
One of the reason for including this feature in Java 15 is
Code snippet To generate key pair and sign the same
KeyPairGenerator kpg = KeyPairGenerator.getInstance("Ed25519");
KeyPair kp = kpg.generateKeyPair();
// algorithm is pure Ed25519
Signature sig = Signature.getInstance("Ed25519");
sig.initSign(kp.getPrivate());
sig.update(msg);
byte[] s = sig.sign();
Code snippet Snippet to construct the public key
KeyFactory kf = KeyFactory.getInstance("EdDSA");
boolean xOdd = ...
BigInteger y = ...
NamedParameterSpec paramSpec = new NamedParameterSpec("Ed25519");
EdECPublicKeySpec pubSpec = new EdECPublicKeySpec(paramSpec, new EdPoint(xOdd, y));
PublicKey pubKey = kf.generatePublic(pubSpec);
More reading material : https://openjdk.java.net/jeps/339
Primary objective of Sealed classes is to create a way for Java to support algebraic data types .
When we look at Java class hierarchy which enables re use of code by inheritance but Author of Sealed Classes Brian Goetz says however its not always for the purpose of code re-use but also to model various possibilities that exists in a domain.
For example if author of a class called Shape
may intend that only particular classes can extend the shape but not every class thereby author of Shape
class can write code to known subclasses of the Shape
and do not require to write code to defend for unknown subclasses of Shape
“A sealed class
or interface
can be extended or implemented only by those classes and interfaces permitted to do so” -https://openjdk.java.net/jeps/360
Example:
package com.example.geometry;
public abstract sealed class Shape
permits Circle, Rectangle, Square {...}
In the above code snippet class Shape
is marked as sealed class by applying the modifier sealed
to its declaration and permits
clause specifies the classes allowed to extend the sealed class. Here only Circle, Rectange and Square
can extend the Shape
class.
Main purpose of sealing a class is to let client code reason about all permitted subclasses. Traditionally way to reason about subclasses is with an if-else chain of instanceof
Example :
int getCenter(Shape shape) {
if (shape instanceof Circle) {
return ... ((Circle)shape).center() ...
} else if (shape instanceof Rectangle) {
return ... ((Rectangle)shape).length() ...
} else if (shape instanceof Square) {
return ... ((Square)shape).side() ...
}
}
Using the permits
clause and letting compiler know allowed classes can extend a sealed class along with pattern matching, Instead of inspecting an instance of a sealed class with if-else
, client code will be able to use switch
over using the JEP 375 test patterns. This allows compiler to check that the patterns are exhaustive
Example:
int getCenter(Shape shape) {
return switch (shape) {
case Circle c -> ... c.center() ...
case Rectangle r -> ... r.length() ...
case Square s -> ... s.side() ...
};
}
More reading material :https://openjdk.java.net/jeps/360
Hidden classes are for JVM and not aimed to change java language in any way. Hidden classes intention mainly aimed for the frameworks that generates classes at run time and use them via Reflection.
A hidden class can be unloaded when it is no longer reachable, or it can share the lifetime of a class loader so that it is unloaded only when the class loader is garbage collected.
Example code for creating a hidden class
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.Base64;
public class Test {
static final String testString = "ABC";
public static void main(String[] args) throws Throwable {
byte[] testStringInBytes = Base64.getDecoder().decode(testString);
Class<?> proxy = MethodHandles.lookup()
.defineHiddenClass(testStringInBytes,
true, MethodHandles.Lookup.ClassOption.NESTMATE)
.lookupClass();
System.out.println(proxy.getName());
}
}
Important difference in how a hidden class is created is the name it gets on creation. A hidden class is not anonymous. Name of the class can be fetched via Class::getName
and name has a sufficiently unusual form that it effectively makes the class invisible to all other classes. The name is the concatenation of:
this_class
in the ClassFile
structure, say A/B/C
;'.'
character; andFor example it could look like com.rockey.Test/0x0000000800b94440
More reading material: https://openjdk.java.net/jeps/371
This feature reimplements java.net.DatagramSocket
and java.net.MulticastSocket
APIs as their implementation dates back to JDK 1.0 which has a mix of both Java and C that was difficult to maintain for the authors. Authors idea is these new implementation will adapt well to work with virtual threads ( this is explored as Project loom). These reimplementation are enabled by default providing non-interruptible behaviour for datagram and multicast sockets by directly using the platform-default implementation of the selector provider (sun.nio.ch.SelectorProviderImpl
and sun.nio.ch.DatagramChannelImpl
)
More reading material:https://openjdk.java.net/jeps/373
With this JEP Java might start to embrace pattern matching like Many languages, from Haskell to C#, have embraced pattern matching for its brevity and safety. Pattern matching allows the desired ‘shape’ of an object to be expressed concisely (the pattern)
This is an enhancement in the Java programming language with pattern matching for the instanceof
operator. Pattern matching allows common logic in a program, namely the conditional extraction of components from objects, to be expressed more concisely and safely.
Main driving factor behind this Nearly every program has a sort of logic that combines testing if an expression has a certain type or structure, and then conditionally extracting components of its state for further processing.
Example: instanceof-and-cast idiom (without using pattern)
if (obj instanceof String) {
String s = (String) obj;
// use s
}
Three things happening here so we can use the string value:
a test (is obj
a String
?)
a conversion (casting obj
to String
) and
the declaration of a new local variable (s
)
It is tedious; doing both the type test and cast should be unnecessary. But most importantly, the repetition provides opportunities for errors to creep unnoticed into programs. To simplify above code using patterns first we need to see what a pattern is “A pattern is a combination of (1) a predicate that can be applied to a target, and (2) a set of binding variables that are extracted from the target only if the predicate successfully applies to it.”
Above code now can be written as
Example: simplifying the above code (Scope of s is restricted to if block)
if (obj instanceof String s) {
// can use s here
} else {
// can't use s here
}
if obj
is an instance of String
, then it is cast to String
and assigned to the binding variable s
. The binding variable is in scope in the true block of the if
statement, and not in the false block of the if
statement but this scope of the variable is dependent on the by the semantics of the containing expressions and statements. For example, in this code below will extend scope of s to else block as well depending on the condition evaluation
Example: (Scope of s is extended to else block)
if (!(obj instanceof String s)) {
.. s.contains(..) ..
} else {
.. s.contains(..) ..
}
Additional reading material: https://openjdk.java.net/jeps/375
This JEP essentially changes the Z Garabage collector from an experimental feature to a product feature. With the positive feedback received from community on this JEP when introduced in Java 11 JDK authors added a number of features and enhancements. Which are
-XX:SoftMaxHeapSize
Furthermore, all commonly used platforms are now supported:
ZGC is enabled by setting
-XX:+UnlockExperimentalVMOptions -XX:+UseZGC
This JEP will allow to add text blocks to the Java language. A text block is a multi-line string literal that avoids the necessity of escape sequences and automatically formats the string in a predictable way. Not only that but also addition of new methods to string to support text blocks
String::stripIndent()
: used to strip away incidental white space from the text block contentString::translateEscapes()
: used to translate escape sequencesString::formatted(Object... args)
: simplify value substitution in the text blockUsing “one-dimensional” string literals
String html = "<html>\n" +
" <body>\n" +
" <p>Hello, world</p>\n" +
" </body>\n" +
"</html>\n";
Using a “two-dimensional” block of text
String html = """
<html>
<body>
<p>Hello, world</p>
</body>
</html>
""";
Using “one-dimensional” string literals
String query = "SELECT \"EMP_ID\", \"LAST_NAME\" FROM \"EMPLOYEE_TB\"\n" +
"WHERE \"CITY\" = 'INDIANAPOLIS'\n" +
"ORDER BY \"EMP_ID\", \"LAST_NAME\";\n";
Using a “two-dimensional” block of text
String query = """
SELECT "EMP_ID", "LAST_NAME" FROM "EMPLOYEE_TB"
WHERE "CITY" = 'INDIANAPOLIS'
ORDER BY "EMP_ID", "LAST_NAME";
""";
This JEP essentially changes the Shenandoah Garabage collector from an experimental feature to a product feature
To enable this Garbage collection command to use is java -XX:+UseShenandoahGC
This JEP removes the source code and build support for the Solaris/SPARC, Solaris/x64, and Linux/SPARC ports.Main reason behind dropping support for the Solaris and SPARC ports will enable contributors in the OpenJDK Community to accelerate the development of new features that will move the platform forward
This feature will Introduce an API to allow Java programs to safely and efficiently access foreign memory outside of the Java heap.
Many Java programs access foreign memory, such as Ignite, mapDB, memcached, Lucene, and Netty’s ByteBuf API. There are 3 ways to use Java API’s prior to version 15 to access foreign memory which are
But when accessing foreign memory, there is a dilemma: if one should they choose a safe but limited (and possibly less efficient) path, such as the ByteBuffer
API, or should they abandon safety guarantees and embrace the dangerous and unsupported Unsafe
API so this JEP aimed to provide solution for safe, supported, and efficient API for foreign memory access by introducing three main abstractions: MemorySegment
, MemoryAddress
, and MemoryLayout
:
A MemorySegment
models a contiguous memory region with given spatial and temporal bounds.
A MemoryAddress
models an address. There are generally two kinds of addresses: A checked address is an offset within a given memory segment, while an unchecked address is an address whose spatial and temporal bounds are unknown, as in the case of a memory address obtained – unsafely – from native code.
A MemoryLayout
is a programmatic description of a memory segment’s contents.
Example snippet to create native MemorySegment and this will create a memory buffer of 100 bytes
try (MemorySegment segment = MemorySegment.allocateNative(100)) {
...
}
Example code:
import jdk.incubator.foreign.MemoryAddress;
import java.lang.invoke.VarHandle;
import jdk.incubator.foreign.MemoryHandles;
import java.nio.ByteOrder;
import jdk.incubator.foreign.MemorySegment;
public class ForeignMemoryAccess {
public static void main(String[] args) {
VarHandle handle = MemoryHandles.varHandle(
int.class, ByteOrder.nativeOrder());
try (MemorySegment segment = MemorySegment.allocateNative(2048)) {
MemoryAddress base = segment.baseAddress();
// print memory address
System.out.println(base);
}
}
}
Additional reading: https://openjdk.java.net/jeps/383
Records are first introduced in Java 14 and this is a JEP introduced feature to support sealed types, local records, annotation on records. Records are classes that act as transparent carriers for immutable data. Records can be thought of as nominal tuples.
Main motivation for records
feature is based on the complaint that “Java is too verbose” or has “too much ceremony” .Classes that are nothing more than immutable data carriers for a handful of values can be taken as a good example to demonstrate this, Properly writing a data-carrier class involves a lot of low-value, repetitive, error-prone code: constructors, accessors, equals
, hashCode
, toString
, etc
Example: (Before Records)
public class Message {
private final String message;
private final long dimensionl;
Message(String message, long dimensionl) {
this.message = message;
this.dimensionl = dimensionl;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Message message1 = (Message) o;
return dimensionl == message1.dimensionl &&
Objects.equals(message, message1.message);
}
@Override
public int hashCode() {
return Objects.hash(message, dimensionl);
}
@Override
public String toString() {
return "Message{" +
"message='" + message + '\'' +
", dimensionl=" + dimensionl +
'}';
}
}
Omitting some methods such as equals
leads to wrong behaviour during comparison of objects and difficult to debug.
Records are aimed to solve some these problems. They are a new kind of class in java language and its purpose is to declare that a small group of variables is to be regarded as a new kind of entity. A record declares its state – the group of variables – and commits to an API that matches that state
Example: (Using Records)
record Message(String message, long dimensionl) { }
Record anatomy
record
specifies a name, a header, and a body. The header lists the components of the record, which are the variables that make up its state
Example
record Point(int x, int y) { }
Name here is Point
Header is x
and y
Body is content between curly braces
record
by default comes with four standard members
public
accessor method with the same name and return type as the component, and a private
final
field with the same type as the componentequals
and hashCode
methodstoString
The combination of records and sealed types is sometimes referred to as algebraic data types.
Records allow to express product types, and sealed types allow to express sum types
Example:
package com.example.expression;
public sealed interface Expr
permits ConstantExpr, PlusExpr, TimesExpr, NegExpr {...}
public record ConstantExpr(int i) implements Expr {...}
public record PlusExpr(Expr a, Expr b) implements Expr {...}
public record TimesExpr(Expr a, Expr b) implements Expr {...}
public record NegExpr(Expr e) implements Expr {...}
Since Distributed systems have been based on web technology for a very long time. Concerns about traversing firewalls, filtering requests, authentication, and security have all been addressed in the web services space. Lazy instantiation of resources is handled by load balancers, orchestration, and containers. None of these mechanisms is present in the RMI Activation model for distributed systems and Java team found no evidence of any new applications being written to use RMI Activation.
RMI Activation was made optional in Java 8 and removed in Java 15
This is a legacy synchronization optimization of biased locking, which is costly to maintain hence JDK teams are disabling it by default.
Legacy collection API’s such as Hashtable and Vector made use of these performance gains but in the current trend most newer applications generally use the non-synchronized collections such as HashMap
and ArrayList
or even more-performant concurrent data structures, introduced in Java 5, for multi-threaded scenarios
With this JEP, biased locking will no longer be enabled when HotSpot is started unless -XX:+UseBiasedLocking
is set on the command line.
More reading material:https://openjdk.java.net/jeps/374
This is removal from the Java and it should not affect the way javax.script
API
More reading material: https://openjdk.java.net/jeps/372
powerctl is a little weekend project I put together to provide a simple tool for managing power states on Linux. I had previously put my laptop into suspend with a basic “echo mem | doas tee /sys/power/state”, but this leaves a lot to be desired. I have to u…
via Drew DeVault's blog August 28, 2022Links to exhibits from the Microsoft anti-trust case, with a bit of info on each link. Projection of PC marketshare Share of new browser users Share of the browser market, grouped by major ISP, 3 month moving average Share of the browser market, grouped by ma…
via danluu.com August 24, 2022Hi all! This month I’ve been pondering offline-first apps. The online aspect of modern apps is an important feature for many use-cases: it enables collaboration between multiple people and seamless transition between devices (e.g. I often switch between my pe…
via emersion August 14, 2022Generated by openring