Instruction file imported from dmorav1/copilot_template (
.github/instructions/java-25-features.instructions.md). Copyright stays with the author.
Java 25 Features
General Instructions
- Enable Preview Features: Many Java 25 features (Primitive Patterns, Module Imports, Structured Concurrency, etc.) are in preview or incubator status. Ensure the project is configured to enable them if used (
--enable-preview,--add-modules). - Target Version: Set the compiler release to 25 (
--release 25). - Stability Warning: Be aware that preview APIs may change in future releases. Use them for modernizing code but note the experimental nature in comments if necessary.
Best Practices
Language Improvements
-
Primitive Types in Patterns: When using
switchorinstanceof, match primitive types directly (e.g.,case int i,instanceof double d) to eliminate unnecessary boxing/unboxing and boilerplate type checks.static void test(Object obj) { if (obj instanceof int i) { System.out.println("It's an int: " + i); } } -
Module Imports: Use
import module [module.name](e.g.,import module java.base) to succinctly import all packages exported by a module.- Ambiguity Handling: Be vigilant about name collisions (e.g.,
Dateinjava.utilvsjava.sql). Always resolve ambiguities with explicit single-class imports for the colliding types.
import module java.base; //... public class Main { public static void main(String[] args) { Date d = new Date(); // Resolved from java.base (java.util.Date) System.out.println("Resolved Date: " + d); } } - Ambiguity Handling: Be vigilant about name collisions (e.g.,
-
Compact Source Files: For simple scripts, internal tools, prototypes, or learning exercises, use top-level instance
mainmethods without a wrapping class declaration.void main() { System.out.println("Hello from Java 25!"); } -
Flexible Constructor Bodies: Place validation logic, argument preparation, or logging before the
super(...)orthis(...)call in constructors. This improves "fail-fast" behavior and prevents unnecessary superclass initialization when arguments are invalid.class Employee extends Person { final String name; Employee(String name, int age) { if (age < 18 || age > 67) throw new IllegalArgumentException("Age must be between 18 and 67"); super(age); // super() is no longer required as the first statement this.name = name; } }
API Enhancements
-
Scoped Values: Prefer
ScopedValue<T>overThreadLocal<T>for passing implicit context, especially when using Virtual Threads.- Use
ScopedValue.where(KEY, value).run(...)to bind values for a specific execution scope. - Ensure logic accessing the value is strictly within the runnable/callable scope.
static final ScopedValue<String> USER = ScopedValue.newInstance(); // inside a method... ScopedValue.where(USER, "Alice").run(() -> { System.out.println("User: " + USER.get()); }); - Use
-
Structured Concurrency: Use
StructuredTaskScopeto manage related concurrent tasks as a single unit. This ensures proper error handling, cancellation propagation, and observability.- Prefer
StructuredTaskScope.open()(JEP 505) over older constructors. - Always usage
try-with-resourcesto ensure the scope is closed (joined) correctly.
try (var scope = StructuredTaskScope.<String>open()) { var userTask = scope.fork(() -> fetchUser()); var orderTask = scope.fork(() -> fetchOrder()); scope.join(); System.out.println(userTask.get() + " - " + orderTask.get()); } - Prefer
-
Stable Values: Use
StableValue<T>for values that are set once and effectively immutable (like lazy initialization constants). This offers better performance and safety than double-checked locking or standard lazy initialization patterns.var greeting = StableValue.<String>of(); String message = greeting.orElseSet(() -> "Hello from StableValue!"); -
PEM API: Use the standard
java.securitymethods to read and write PEM-encoded keys and certificates directly. Avoid external libraries (like Bouncy Castle) for standard PEM operations unless advanced features are required.String pem = "...PEM CONTENT..."; String base64 = pem.replaceAll("-----.*-----", "").replaceAll("\\s", ""); byte[] keyBytes = Base64.getDecoder().decode(base64); X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes); KeyFactory factory = KeyFactory.getInstance("RSA"); PublicKey key = factory.generatePublic(spec);
Security & Cryptography
-
Key Derivation: Use
SecretKeyFactorywith standard algorithms (e.g., PBKDF2, Scrypt) available in the standard library andjavax.crypto(JEP 510) instead of custom implementations or older legacy approaches.PBEKeySpec spec = new PBEKeySpec(password, salt, 65536, 256); SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256"); SecretKey key = factory.generateSecret(spec);
Performance & Profiling
-
Vector API: For performance-critical numerical computations, consider the Vector API (
jdk.incubator.vector) to leverage SIMD instructions.- Note: This requires the
jdk.incubator.vectormodule to be added.
var species = FloatVector.SPECIES_128; var a = FloatVector.fromArray(species, left, 0); var b = FloatVector.fromArray(species, right, 0); var c = a.add(b); c.intoArray(result, 0); - Note: This requires the
-
JFR Profiling: Utilize new JFR events for CPU time and method profiling (
-XX:StartFlightRecording=...) to diagnose performance issues in production or testing environments.