Generated banner for Java Upgrade
Generated logo for Java Upgrade

Java Upgrade

GitHubOfficialFirst committed by jduboisDistributed by GitHub

  • Skill
  • Guidelines
  • Software development
  • MIT
  • Low risk

From the author

Upgrade a Java project to a newer JDK (11 to 17, 17 to 21, 21 to 25) and adopt the new language and library features. Detects the current Java version from the build files, then loads only the matching per-version guide. Use when the user asks to upgrade Java, modernize Java code, or adopt records, sealed classes, pattern matching, virtual threads or other new Java features. For automated end-to-end upgrades see the modernize-java plugin.
— the description by jdubois

AutoGineer summary

Written by AutoGineer from the skill’s own files, not by the author

This skill provides a structured approach to upgrading Java applications across LTS versions (11 to 17, 17 to 21, 21 to 25). It helps developers assess their current environment, update build configurations, and incrementally adopt modern Java features like records, sealed classes, pattern matching, and virtual threads. The skill emphasizes safety by recommending small, reviewable changes and provides specific migration patterns for each version transition.

What it does

  • Detects current Java versions from build files like pom.xml and build.gradle.
  • Provides version-specific upgrade guides for LTS transitions.
  • Offers code migration patterns for modern Java features.
  • Advises on updating build configurations and dependencies.
  • Integrates with external tools like OpenRewrite for automated refactoring.
  • Provides guidance on handling namespace migrations such as javax to jakarta.

When to use it

  • When you need to upgrade a Java project to a newer JDK version.
  • When you want to modernize existing Java code with newer language features.
  • When you are unsure of the specific migration steps between two Java LTS versions.
  • When you need to adopt features like records, sealed classes, or virtual threads.

Advantages

  • Provides a clear, step-by-step path for complex version upgrades.
  • Focuses on safe, incremental refactoring to minimize breaking changes.
  • Includes specific code examples for modern Java syntax.
  • Helps identify necessary build tool and dependency updates.
  • Directs users to official documentation and migration resources.

Usage examples

“How do I upgrade my Java 11 project to Java 17?”
The agent will analyze your build files, outline the steps for the 11 to 17 transition, and provide specific migration patterns for features like records and sealed classes.
“Show me how to use virtual threads in my Java 21 project.”
The agent will explain the transition from platform threads to virtual threads using Executors.newVirtualThreadPerTaskExecutor and provide a code example.
“How can I use pattern matching for switch in Java 21?”
The agent will provide a comparison between traditional switch statements and the new pattern matching syntax, including examples with guarded patterns.

Install

  1. Download the skill as a ZIP:

    https://mp-staging.autogineer.com/agent-skills/github/java-upgrade/download
  2. Unzip it into the cross-client skills folder of your project:

    mkdir -p .agents/skills && unzip java-upgrade.zip -d .agents/skills

Documentation by jdubois

Java Upgrade

Help upgrade a Java project step by step and adopt new language features safely.

Need a more powerful, automated upgrade?

The Microsoft plugin modernize-java ("GitHub Copilot modernization - Java Upgrade CLI Plugin", also listed in this repository's plugins/external.json) upgrades Java applications from the command line. It can:

  • analyze the project, assess dependencies and generate a customizable upgrade plan you can review and edit;

  • execute the plan to automatically transform the codebase;

  • fix build issues and resolve migration errors during the upgrade, running test validations;

  • validate the application against known CVEs after the upgrade;

  • output a detailed summary (file changes, updated dependencies, test results, remaining issues) suitable for PRs or CI logs.

This skill provides lightweight, step-by-step guidance and works without the plugin; the plugin is the more powerful option for large or multi-step upgrades. It is a separate Microsoft plugin, not part of this repository's skills. To try it (see the plugin README for the recommended model):

npm install -g @github/copilot
copilot plugin marketplace add microsoft/modernize-java
copilot plugin install modernize-java@modernize-java
copilot --agent modernize-java:modernize-java

Workflow

  1. Detect the current and target versions. Read pom.xml (maven.compiler.release, java.version, toolchains), build.gradle* (java.toolchain, sourceCompatibility) and CI files. Never assume a target: ask the user if it is not stated.

  2. Upgrade one LTS at a time (11 → 17 → 21 → 25). Build and run the tests after each step.

  3. Load only the guide you need from the table below. Do not load all of them.

  4. Update the build first (compiler release, plugins, dependencies that need newer versions), then adopt language features incrementally in small, reviewable changes.

  5. Verify: ./mvnw verify or ./gradlew build, and fix deprecation and removal warnings before adopting new features.

  6. Automate and check upstream. Use OpenRewrite when available (for example org.openrewrite.java.migrate.UpgradeToJava21) and always fetch the version-specific official guide before editing; review the generated diff.

Upstream sources (read before upgrading)

Fetch the page for the target version; prefer it over this skill when they conflict. Full list with build-tool and agent compatibility pages: references/upstream-sources.md.

Per-version guides

UpgradeGuide
Java 11 → 17references/java-11-to-17.md
Java 17 → 21references/java-17-to-21.md
Java 21 → 25references/java-21-to-25.md

Rules

  • Prefer behavior-preserving refactorings; do not change public APIs unless asked.

  • Detect the project's frameworks (Spring Boot, Quarkus, Jakarta EE) and check their minimum supported Java version before raising the target.

  • Do not adopt preview features unless the user opts in and the build enables them explicitly.

  • For javax.* to jakarta.* namespace changes, use the javax-to-jakarta-migration skill if it is installed (gh skills install github/awesome-copilot javax-to-jakarta-migration); otherwise do the migration inline: update imports, META-INF service files and XML descriptors, and bump dependencies to Jakarta EE 9+ equivalents.

  • If the modernize-java plugin is installed and the user wants an automated upgrade, defer to it and use this skill for the per-version guides and the javax to jakarta safeguards.

Files

references/java-11-to-17.md

Java 11 to Java 17 Upgrade Guide

Project Context

This guide provides comprehensive GitHub Copilot instructions for upgrading Java projects from JDK 11 to JDK 17, covering major language features, API changes, and migration patterns based on 47 JEPs integrated between these versions.

Language Features and API Changes

JEP 395: Records (Java 16)

Migration Pattern: Convert data classes to records

// Old: Traditional data class
public class Person {
    private final String name;
    private final int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String name() { return name; }
    public int age() { return age; }

    @Override
    public boolean equals(Object obj) { /* boilerplate */ }
    @Override
    public int hashCode() { /* boilerplate */ }
    @Override
    public String toString() { /* boilerplate */ }
}

// New: Record (Java 16+)
public record Person(String name, int age) {
    // Compact constructor for validation
    public Person {
        if (age < 0) throw new IllegalArgumentException("Age cannot be negative");
    }

    // Custom methods can be added
    public boolean isAdult() {
        return age >= 18;
    }
}
JEP 409: Sealed Classes (Java 17)

Migration Pattern: Use sealed classes for restricted inheritance

// New: Sealed hierarchy (all permitted types must be in the same package/module;
// top-level public types each live in their own file)
public sealed interface Shape permits Circle, Rectangle, Triangle {
    double area();
}

public record Circle(double radius) implements Shape {
    @Override
    public double area() {
        return Math.PI * radius * radius;
    }
}

public record Rectangle(double width, double height) implements Shape {
    @Override
    public double area() {
        return width * height;
    }
}

// non-sealed allows further inheritance; a record is implicitly final,
// so use a class if the type must stay open for extension
public non-sealed class Triangle implements Shape {
    private final double base, height;

    public Triangle(double base, double height) {
        this.base = base;
        this.height = height;
    }

    public double base() { return base; }

    @Override
    public double area() {
        return 0.5 * base * height;
    }
}
JEP 394: Pattern Matching for instanceof (Java 16)

Migration Pattern: Simplify instanceof checks

// Old: Traditional instanceof with casting
public String processObject(Object obj) {
    if (obj instanceof String) {
        String str = (String) obj;
        return str.toUpperCase();
    } else if (obj instanceof Integer) {
        Integer num = (Integer) obj;
        return "Number: " + num;
    } else if (obj instanceof List<?>) {
        List<?> list = (List<?>) obj;
        return "List with " + list.size() + " elements";
    }
    return "Unknown type";
}

// New: Pattern matching for instanceof (Java 16+)
public String processObject(Object obj) {
    if (obj instanceof String str) {
        return str.toUpperCase();
    } else if (obj instanceof Integer num) {
        return "Number: " + num;
    } else if (obj instanceof List<?> list) {
        return "List with " + list.size() + " elements";
    }
    return "Unknown type";
}

// Works great with sealed classes
public String describeShape(Shape shape) {
    if (shape instanceof Circle circle) {
        return "Circle with radius " + circle.radius();
    } else if (shape instanceof Rectangle rect) {
        return "Rectangle " + rect.width() + "x" + rect.height();
    } else if (shape instanceof Triangle triangle) {
        return "Triangle with base " + triangle.base();
    }
    return "Unknown shape";
}
JEP 361: Switch Expressions (Java 14)

Migration Pattern: Convert switch statements to expressions

// Old: Traditional switch statement
public String getDayType(DayOfWeek day) {
    String result;
    switch (day) {
        case MONDAY:
        case TUESDAY:
        case WEDNESDAY:
        case THURSDAY:
        case FRIDAY:
            result = "Workday";
            break;
        case SATURDAY:
        case SUNDAY:
            result = "Weekend";
            break;
        default:
            throw new IllegalArgumentException("Unknown day: " + day);
    }
    return result;
}

// New: Switch expression (Java 14+)
public String getDayType(DayOfWeek day) {
    return switch (day) {
        case MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY -> "Workday";
        case SATURDAY, SUNDAY -> "Weekend";
    };
}

// With yield for complex logic
public int calculateScore(Grade grade) {
    return switch (grade) {
        case A -> 100;
        case B -> 85;
        case C -> 70;
        case D -> {
            System.out.println("Consider improvement");
            yield 55;
        }
        case F -> {
            System.out.println("Needs retake");
            yield 0;
        }
    };
}
JEP 406: Pattern Matching for switch (Preview in Java 17)

Migration Pattern: Enhanced switch with patterns (Preview feature)

// Requires --enable-preview flag
public String formatValue(Object obj) {
    return switch (obj) {
        case String s -> "String: " + s;
        case Integer i -> "Integer: " + i;
        case null -> "null value";
        default -> "Unknown: " + obj.getClass().getSimpleName();
    };
}

// With guarded patterns (Java 17 preview syntax: && guard; the "when" keyword arrived in Java 21)
public String categorizeNumber(Object obj) {
    return switch (obj) {
        case Integer i && i < 0 -> "Negative integer";
        case Integer i && i == 0 -> "Zero";
        case Integer i && i > 0 -> "Positive integer";
        case Double d && d.isNaN() -> "Not a number";
        case Number n -> "Other number: " + n;
        case null -> "null";
        default -> "Not a number";
    };
}
JEP 378: Text Blocks (Java 15)

Migration Pattern: Use text blocks for multi-line strings

// Old: Concatenated strings
String html = "<html>\n" +
              "  <body>\n" +
              "    <h1>Hello World</h1>\n" +
              "    <p>Welcome to Java 17!</p>\n" +
              "  </body>\n" +
              "</html>";

String sql = "SELECT p.id, p.name, p.email, " +
             "       a.street, a.city, a.state " +
             "FROM person p " +
             "JOIN address a ON p.address_id = a.id " +
             "WHERE p.active = true " +
             "ORDER BY p.name";

// New: Text blocks (Java 15+)
String html = """
              <html>
                <body>
                  <h1>Hello World</h1>
                  <p>Welcome to Java 17!</p>
                </body>
              </html>
              """;

String sql = """
             SELECT p.id, p.name, p.email,
                    a.street, a.city, a.state
             FROM person p
             JOIN address a ON p.address_id = a.id
             WHERE p.active = true
             ORDER BY p.name
             """;

// With string interpolation methods
String json = """
              {
                "name": "%s",
                "age": %d,
                "city": "%s"
              }
              """.formatted(name, age, city);
JEP 358: Helpful NullPointerExceptions (Java 14)

Migration Guidance: Better NPE debugging (enabled by default in Java 17)

// Old NPE message: "Exception in thread 'main' java.lang.NullPointerException"
// New NPE message shows exactly what was null:
// "Cannot invoke 'String.length()' because the return value of 'Person.getName()' is null"

public class PersonProcessor {
    public void processPersons(List<Person> persons) {
        // This will show exactly which person.getName() returned null
        persons.stream()
            .mapToInt(person -> person.getName().length())  // Clear NPE if getName() returns null
            .sum();
    }

    // Better error messages help with complex expressions
    public void complexExample(Map<String, List<Person>> groups) {
        // NPE will show exactly which part of the chain is null
        int totalNameLength = groups.get("admins")
                                  .get(0)
                                  .getName()
                                  .length();
    }
}
JEP 371: Hidden Classes (Java 15)

Migration Pattern: Use for framework and proxy generation

// For frameworks creating dynamic proxies
public class DynamicProxyExample {
    public static <T> T createProxy(Class<T> interfaceClass, InvocationHandler handler) {
        // Hidden classes provide better encapsulation for dynamically generated classes
        MethodHandles.Lookup lookup = MethodHandles.lookup();

        // Framework code would use hidden classes for better isolation
        // This is typically handled by frameworks, not application code
        return interfaceClass.cast(
            Proxy.newProxyInstance(
                interfaceClass.getClassLoader(),
                new Class<?>[]{interfaceClass},
                handler
            )
        );
    }
}
JEP 334: JVM Constants API (Java 12)

Migration Pattern: Use for compile-time constants

import java.lang.constant.*;

// For advanced metaprogramming and tooling: nominal descriptors of classes and methods
public class ConstantExample {
    public static void main(String[] args) {
        ClassDesc stringDesc = ClassDesc.of("java.lang.String");
        MethodTypeDesc lengthDesc = MethodTypeDesc.of(ConstantDescs.CD_int);
        System.out.println(stringDesc.descriptorString()); // Ljava/lang/String;
        System.out.println(lengthDesc.descriptorString()); // ()I
    }
}
JEP 415: Context-Specific Deserialization Filters (Java 17)

Migration Pattern: Enhanced security for object deserialization

import java.io.*;

public class SecureDeserialization {
    // Set up deserialization filters for security
    public static void setupSerializationFilters() {
        // Global filter
        ObjectInputFilter globalFilter = ObjectInputFilter.Config.createFilter(
            "java.base/*;java.util.*;!*"
        );
        ObjectInputFilter.Config.setSerialFilter(globalFilter);
    }

    public <T> T deserializeSecurely(byte[] data, Class<T> expectedType) throws IOException, ClassNotFoundException {
        try (ByteArrayInputStream bis = new ByteArrayInputStream(data);
             ObjectInputStream ois = new ObjectInputStream(bis)) {

            // Context-specific filter
            ObjectInputFilter contextFilter = ObjectInputFilter.Config.createFilter(
                expectedType.getName() + ";java.lang.*;!*"
            );
            ois.setObjectInputFilter(contextFilter);

            return expectedType.cast(ois.readObject());
        }
    }
}
JEP 356: Enhanced Pseudo-Random Number Generators (Java 17)

Migration Pattern: Use new random generator interfaces

import java.util.random.*;

// Old: Limited Random class
Random oldRandom = new Random();
int oldValue = oldRandom.nextInt(100);

// New: Enhanced random generators (Java 17+)
RandomGenerator generator = RandomGeneratorFactory
    .of("Xoshiro256PlusPlus")
    .create(System.nanoTime());

RandomGenerator.SplittableGenerator splittableGenerator =
    RandomGeneratorFactory.<RandomGenerator.SplittableGenerator>of("L64X128MixRandom").create();

// Better for parallel processing
splittableGenerator.splits(4)
    .parallel()
    .mapToInt(rng -> rng.nextInt(1000))
    .forEach(System.out::println);

// Streamable random values
generator.ints(10, 1, 101)
    .forEach(System.out::println);

I/O and Networking Improvements

JEP 380: Unix-Domain Socket Channels (Java 16)

Migration Pattern: Use Unix domain sockets for local IPC

import java.io.IOException;
import java.net.StandardProtocolFamily;
import java.net.UnixDomainSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.file.Path;

// Old: TCP sockets for local communication
// ServerSocketChannel server = ServerSocketChannel.open();
// server.bind(new InetSocketAddress("localhost", 8080));

// New: Unix domain sockets (Java 16+)
public class UnixSocketExample {
    public void createUnixDomainServer() throws IOException {
        Path socketPath = Path.of("/tmp/my-app.socket");
        UnixDomainSocketAddress address = UnixDomainSocketAddress.of(socketPath);

        try (ServerSocketChannel server = ServerSocketChannel.open(StandardProtocolFamily.UNIX)) {
            server.bind(address);

            while (true) {
                try (SocketChannel client = server.accept()) {
                    // Handle client connection
                    handleClient(client);
                }
            }
        }
    }

    public void connectToUnixSocket() throws IOException {
        Path socketPath = Path.of("/tmp/my-app.socket");
        UnixDomainSocketAddress address = UnixDomainSocketAddress.of(socketPath);

        try (SocketChannel client = SocketChannel.open(address)) {
            // Communicate with server
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            client.read(buffer);
        }
    }

    private void handleClient(SocketChannel client) throws IOException {
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        int bytesRead = client.read(buffer);
        // Process client data
    }
}
JEP 352: Non-Volatile Mapped Byte Buffers (Java 14)

Migration Pattern: Use for persistent memory operations

import java.io.IOException;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import jdk.nio.mapmode.ExtendedMapMode; // module jdk.nio.mapmode

public class PersistentMemoryExample {
    public void usePersistentMemory() throws IOException {
        Path nvmFile = Path.of("/mnt/pmem/data.bin");

        try (FileChannel channel = FileChannel.open(nvmFile,
                StandardOpenOption.READ,
                StandardOpenOption.WRITE,
                StandardOpenOption.CREATE)) {

            // Map as persistent memory
            MappedByteBuffer buffer = channel.map(
                FileChannel.MapMode.READ_WRITE, 0, 1024,
                ExtendedMapMode.READ_WRITE_SYNC
            );

            // Write data that persists across crashes
            buffer.putLong(0, System.currentTimeMillis());
            buffer.putInt(8, 12345);

            // Force write to persistent storage
            buffer.force();
        }
    }
}

Build System Configuration

Maven Configuration
<properties>
    <maven.compiler.source>17</maven.compiler.source>
    <maven.compiler.target>17</maven.compiler.target>
    <maven.compiler.release>17</maven.compiler.release>
</properties>

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.11.0</version>
            <configuration>
                <release>17</release>
                <!-- Enable preview features if using JEP 406 -->
                <compilerArgs>
                    <arg>--enable-preview</arg>
                </compilerArgs>
            </configuration>
        </plugin>

        <!-- For running tests with preview features -->
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-surefire-plugin</artifactId>
            <version>3.0.0</version>
            <configuration>
                <argLine>--enable-preview</argLine>
            </configuration>
        </plugin>
    </plugins>
</build>
Gradle Configuration
java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(17)
    }
}

tasks.withType<JavaCompile> {
    options.release.set(17)
    // Enable preview features if needed
    options.compilerArgs.addAll(listOf("--enable-preview"))
}

tasks.withType<Test> {
    useJUnitPlatform()
    // Enable preview features for tests
    jvmArgs("--enable-preview")
}

Deprecations and Removals

JEP 411: Deprecate the Security Manager for Removal

Migration Pattern: Remove Security Manager dependencies

// Old: Using Security Manager
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
    sm.checkPermission(new RuntimePermission("shutdownHooks"));
}

// New: Alternative security approaches
// Use application-level security, containers, or process isolation
// Most applications don't need Security Manager functionality
JEP 398: Deprecate the Applet API for Removal

Migration Pattern: Migrate from Applets to modern web technologies

// Old: Java Applet (deprecated)
public class MyApplet extends Applet {
    @Override
    public void start() {
        // Applet code
    }
}

// New: Modern alternatives
// 1. Convert to standalone Java application
public class MyApplication extends JFrame {
    public MyApplication() {
        setTitle("My Application");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        // Application code
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            new MyApplication().setVisible(true);
        });
    }
}

// 2. Use Java Web Start alternative (jlink)
// 3. Convert to web application using modern frameworks
JEP 372: Remove the Nashorn JavaScript Engine

Migration Pattern: Use alternative JavaScript engines

// Old: Nashorn (removed in Java 17)
// ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn");

// New: Alternative approaches
// 1. Use GraalVM JavaScript engine
ScriptEngine engine = new ScriptEngineManager().getEngineByName("graal.js");

// 2. Use external JavaScript execution
ProcessBuilder pb = new ProcessBuilder("node", "script.js");
Process process = pb.start();

// 3. Use web-based approach or embedded browser

JVM and Performance Improvements

JEP 377: ZGC - A Scalable Low-Latency Garbage Collector (Java 15)

Migration Pattern: Enable ZGC for low-latency applications

# Enable ZGC
-XX:+UseZGC   # Production-ready since Java 15; no experimental unlock needed

# Monitor ZGC performance
-Xlog:gc*:file=gc.log
JEP 379: Shenandoah - A Low-Pause-Time Garbage Collector (Java 15)

Migration Pattern: Enable Shenandoah for consistent latency

# Enable Shenandoah
# Not included in every JDK build (e.g. Oracle JDK); production-ready since Java 15 where available
-XX:+UseShenandoahGC

# Shenandoah tuning
-XX:ShenandoahGCHeuristics=adaptive
JEP 341: Default CDS Archives (Java 12) & JEP 350: Dynamic CDS Archives (Java 13)

Migration Pattern: Improved startup performance

# CDS is enabled by default, but you can create custom archives
# Create custom CDS archive
java -XX:DumpLoadedClassList=classes.lst -cp myapp.jar com.example.Main
java -Xshare:dump -XX:SharedClassListFile=classes.lst -XX:SharedArchiveFile=myapp.jsa -cp myapp.jar

# Use custom CDS archive
java -XX:SharedArchiveFile=myapp.jsa -cp myapp.jar com.example.Main

Testing and Migration Strategy

Phase 1: Foundation (Weeks 1-2)
  1. Update build system

    • Modify Maven/Gradle configuration for Java 17

    • Update CI/CD pipelines

    • Verify dependency compatibility

  2. Address removals and deprecations

    • Remove Nashorn JavaScript engine usage

    • Replace deprecated Applet APIs

    • Update Security Manager usage

Phase 2: Language Features (Weeks 3-4)
  1. Implement Records

    • Convert data classes to records

    • Add validation in compact constructors

    • Test serialization compatibility

  2. Add Pattern Matching

    • Convert instanceof chains

    • Implement type-safe casting patterns

Phase 3: Advanced Features (Weeks 5-6)
  1. Switch Expressions

    • Convert switch statements to expressions

    • Use new arrow syntax

    • Implement complex yield logic

  2. Text Blocks

    • Replace concatenated multi-line strings

    • Update SQL and HTML generation

    • Use formatting methods

Phase 4: Sealed Classes (Weeks 7-8)
  1. Design sealed hierarchies

    • Identify inheritance restrictions

    • Implement sealed class patterns

    • Combine with pattern matching

  2. Testing and validation

    • Comprehensive test coverage

    • Performance benchmarking

    • Compatibility verification

Performance Considerations

Records vs Traditional Classes
  • Records are more memory efficient

  • Faster creation and equality checks

  • Automatic serialization support

  • Consider for data transfer objects

Pattern Matching Performance
  • Eliminates redundant type checks

  • Reduces casting overhead

  • Better JVM optimization opportunities

  • Use with sealed classes for exhaustiveness

Switch Expressions Optimization
  • More efficient bytecode generation

  • Better constant folding

  • Improved branch prediction

  • Use for complex conditional logic

Best Practices

  1. Use Records for Data Classes

    • Immutable data containers

    • API data transfer objects

    • Configuration objects

  2. Apply Pattern Matching Strategically

    • Replace instanceof chains

    • Use with sealed classes

    • Combine with switch expressions

  3. Adopt Text Blocks for Multi-line Content

    • SQL queries

    • JSON templates

    • HTML content

    • Configuration files

  4. Design with Sealed Classes

    • Domain modeling

    • State machines

    • Algebraic data types

    • API evolution control

  5. Leverage Enhanced Random Generators

    • Parallel processing scenarios

    • High-quality random numbers

    • Statistical applications

    • Gaming and simulation

This comprehensive guide enables GitHub Copilot to provide contextually appropriate suggestions when upgrading Java 11 projects to Java 17, focusing on language enhancements, API improvements, and modern Java development practices.

Requirements and compatibility

Written for: GitHub Copilot

Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta

“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.

Needs: Needs Node.js

Security

What our scanner noticed· Low risk

  • Instructions that use the network

A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.

Attribution and licence

First committed by
GitHub avatar of jduboisjdubois
Publisher
GitHub (Official)
Distributed by
GitHub
Source
github/awesome-copilot · skills/java-upgrade · commit 797337f
Licence
MIT License (redistribution allowed)
  • LICENSE (added by AutoGineer from LICENSE)
  • ATTRIBUTION.md (written by AutoGineer)
Synced
Contributors
jdubois

AutoGineer is not affiliated with or endorsed by jdubois or GitHub.

Download ZIP

The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download java-upgrade.zip