Jabel Documentation

repository·master·Indexed 21 days ago

https://github.com/bsideup/jabel

A javac compiler plugin that enables the use of modern Java 9-14 syntax, such as switch expressions, var, and text blocks, while maintaining compatibility with a Java 8 target. Jabel desugars new language features into valid Java 8 bytecode (major version 52) without changing the classfile version. It provides installation guides for Maven, Gradle 6, and Gradle 7+, and instructions for configuring IntelliJ IDEA to detect Java 9+ API usage.

Tokens
2.2K
Snippets
4
Records
6
Agent score
24%

What's inside Jabel

  1. How Jabel works

    master

    Jabel is a javac compiler plugin that allows you to use modern Java 9-14 syntax (like switch expressions and var declarations) while targeting Java 8.

    It works by instrumenting the Java compiler classes to treat new language features as if they were supported in Java 8. It uses the same desugaring logic as Java 9+ but ensures the resulting classfile version remains compatible with Java 8 (major version 52).

    Note: While you can use new syntax, you must ensure you do not use new Java 9+ APIs (like StackWalker), as Jabel only handles syntactic sugar that can be compiled to Java 8 bytecode.

  2. Install Jabel using Gradle 6 or older

    master

    For Gradle 6 or older, add jabel-javac-plugin as an annotationProcessor. You must configure the compileJava task to set sourceCompatibility (for IDE support) and use compilerArgs to set the --release 8 flag and the -Xplugin:jabel argument.

    Note that on Java 14 and higher, the -Xplugin:jabel argument can be omitted. If using --enable-preview, you may need to filter it out of the arguments during the doFirst phase as shown in the example.

    dependencies {
        annotationProcessor 'com.github.bsideup.jabel:jabel-javac-plugin:0.4.2'
    }
    
    // Add more tasks if needed, such as compileTestJava
    configure([tasks.compileJava]) {
        sourceCompatibility = 14 // for the IDE support
    
        options.compilerArgs = [
                "--release", "8",
                '--enable-preview',
        ]
    
        doFirst {
            // Can be omitted on Java 14 and higher
            options.compilerArgs << '-Xplugin:jabel'
    
            options.compilerArgs = options.compilerArgs.findAll {
                it != '--enable-preview'
            }
        }
    }
  3. Install Jabel using Maven

    master

    To use Jabel with Maven, you need to add jabel-javac-plugin as a dependency and configure the maven-compiler-plugin with the -Xplugin:jabel argument.

    To ensure you don't accidentally use Java 9+ APIs, set the <release> configuration to 8. You should set <source> and <target> to the version of the modern syntax you are using (e.g., 14).

    To verify installation, look for Jabel: initialized. in your Maven build output.

    <dependencies>
        <dependency>
            <groupId>com.github.bsideup.jabel</groupId>
            <artifactId>jabel-javac-plugin</artifactId>
            <version>0.4.1</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
    
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <!-- Make sure we're not using Java 9+ APIs -->
                    <release>8</release>
                    <source>14</source>
                    <target>14</target>
                    <!-- The following setting can be avoided on Java 14 and higher -->
                    <compilerArgs>
                        <arg>-Xplugin:jabel</arg>
                    </compilerArgs>
                </configuration>
            </plugin>
        </plugins>
    </build>
  4. Install Jabel using Gradle 7 and newer

    master

    Gradle 7+ uses toolchains, making configuration straightforward. Add jabel-javac-plugin as both annotationProcessor and compileOnly. Set options.release = 8 and use javaToolchains to specify a higher Java version (e.g., 16) for the compiler.

    If you want to force tests to run with a Java 8 runtime, configure the test task with a Java 8 launcher.

    dependencies {
        annotationProcessor 'com.github.bsideup.jabel:jabel-javac-plugin:0.4.2'
        compileOnly 'com.github.bsideup.jabel:jabel-javac-plugin:0.4.2'
    }
    
    configure([tasks.compileJava]) {
        sourceCompatibility = 16 // for the IDE support
        options.release = 8
    
        javaCompiler = javaToolchains.compilerFor {
            languageVersion = JavaLanguageVersion.of(16)
        }
    }
    
    // Optional: Force tests to run with Java 8
    compileTestJava {
        sourceCompatibility = targetCompatibility = 8
    }
    
    test {
        javaLauncher = javaToolchains.launcherFor {
            languageVersion = JavaLanguageVersion.of(8)
        }
    }
  5. Configure IntelliJ IDEA to detect Java 9+ API usage

    master

    When using --release=8, the compiler will report errors if you use APIs not available in Java 8 (like StackWalker). To have IntelliJ IDEA highlight these usages while editing, configure the language level inspection:

    1. Click on the head with the hat icon in the bottom right of the IDE.
    2. Click on "Configure inspections".
    3. Find "Usages of API which isn't available at the configured language level".
    4. Set it to "Higher than" and select "8 - Lambdas, type annotations etc." from the dropdown.
  6. Use modern Java syntax features with Jabel

    master

    Jabel allows you to write code using modern Java syntax (features from Java 9 through 14) even when your project's target compatibility is set to Java 8. The following features are demonstrated in the example code:

    • Switch Expressions: Using switch as an expression with -> syntax and yield for multi-line blocks.
    • Local Variable Type Inference (var): Using var for local variables and within lambda parameters.
    • Pattern Matching for instanceof: Using if (obj instanceof Type variable) to perform type checks and binding in a single step.
    • Nest-mates: Accessing private members of an outer class from an inner class.
    • Project Coin features:
      • Using @SafeVarargs on private methods.
      • Using effectively final variables in try-with-resources blocks.
    • Text Blocks: Using triple quotes (""") for multi-line string literals.
    // Switch Expressions
    var result = switch (args.length) {
        case 1 -> {
            yield """
                    one...
                    yet pretty long!
            """;
        }
        case 2, 3 -> "two or three";
        default -> "default value";
    };
    
    // Pattern Matching in instanceof
    if (prefix instanceof String s) {
        return s + Integer.toString(0);
    }
    
    // Var in lambda parameter
    Function<Object, String> function = (var prefix) -> {
        if (prefix instanceof String s) {
            return s;
        }
        return "";
    };
    
    // Effectively final variables in try-with-resources
    var closeable = new AutoCloseable() {
        @Override
        public void close() {}
    };
    try (closeable) {
        // use closeable
    }