dev.elide.annotations ships in the bundled dev.elide:elide-api 1.0.0 JAR. It is not published to Maven Central.
Two annotations change what Elide does: @Delicate (Kotlin opt-in) and @Generated (coverage). The others are
markers that a program or library can read; Elide does not act on them.
| Annotation | Kotlin targets | Java targets | Retention | Effect in Elide |
|---|---|---|---|---|
@API | class, type | type, type use | Runtime | None |
@Delicate | class, property, field, local variable, value parameter, constructor, function, getter, setter, typealias | type, field, method, parameter, constructor, local variable | Runtime | Kotlin callers must opt in |
@Eager | class, annotation class | type, annotation type | Class file | None |
@Factory | class | type | Runtime | None |
@Generated | class, function, file, type | type, method, type use | Class file | Left out of JVM coverage reports |
@Inject | Alias of jakarta.inject.Inject | Not available | Runtime | None |
@Nonnull | class, type | type, type use | Runtime | None |
@Nullable | class, type | type, type use | Runtime | None |
@Secret | property, class | type | Runtime | None |
@Singleton | Alias of jakarta. | Not available | Runtime | None |
None of them take parameters. “Class file” retention means the annotation is in the bytecode but not visible to reflection.
Add to a project
| Program | Elide annotations |
|---|---|
Java file run with elide run | On the classpath |
Kotlin file run with elide run | On the classpath |
| Kotlin source set | On the compile and run classpaths |
| Java source set | Declare it: |
dependencies {
maven {
compileOnly { "dev.elide:elide-api:1.0.0" }
}
}The annotations are compiled from Kotlin, so Elide puts the Kotlin standard library on the Java compile classpath
alongside elide-api; Java compiles emit no unknown enum constant warnings.
@API
Marks an interface or abstract class as the API surface of a unit of logic. Readable by reflection.
@API
interface Greeter {
String greet(String name);
}@API
interface Greeter {
fun greet(name: String): String
}@Delicate
Marks an API that callers should use with care. In Kotlin it is a @RequiresOptIn marker: a call without
@OptIn(Delicate::class) fails to compile.
@Delicate
fun reset() {}
@OptIn(Delicate::class)
fun main() {
reset()
}Without the @OptIn:
error: kotlinc: This API is marked delicate; opt in with @OptIn(Delicate::class) if you understand its caveats.Java has no opt-in check, so Java callers compile without it:
@Delicate
static void reset() {}@Eager
Marks a class for eager initialization. Class-file retention only; Elide does not act on it.
@Eager
static class Startup {}@Eager
class Startup@Factory
Marks a class that creates instances of another type. Readable by reflection.
@Factory
static class GreeterFactory {
static Greeter create() {
return name -> "Hello, " + name;
}
}@Factory
object GreeterFactory {
fun create(): Greeter = Polite()
}@Generated
Marks generated code. JVM coverage reports from elide test --coverage and elide run --coverage leave out annotated
classes, methods and Kotlin files (Coverage).
@Generated
static String banner() {
return "generated";
}@Generated
fun banner() = "generated"A whole Kotlin file:
@file:Generated
import dev.elide.annotations.Generated
fun mapAll(xs: List<Int>) = xs.map { it * 2 }@Inject and @Singleton
Kotlin type aliases of jakarta.inject.Inject and jakarta.inject.Singleton. They need
jakarta.inject:jakarta.inject-api on the classpath, which Elide does not add. Elide has no dependency injection
container that reads them; for compile-time injection in Kotlin, use Metro. Java code
cannot import them; use jakarta.inject directly.
dependencies {
maven {
packages { "jakarta.inject:jakarta.inject-api:2.0.1" }
}
}@Singleton
class Polite @Inject constructor() : Greeter {
override fun greet(name: String) = "Hello, $name"
}@Nonnull and @Nullable
Type-use annotations for documenting nullability. Neither compiler checks them, and Kotlin does not read them as
nullability: a Java method returning @Nullable String is still a platform type in Kotlin.
static @Nullable String find(@Nonnull String id) {
return USERS.get(id);
}fun find(id: @Nonnull String): @Nullable String? = users[id]@Secret
Marks a class or property that holds secret material. Readable by reflection; generated toString output is not
redacted. In Java it applies to classes only.
@Secret
record Token(String value) {}data class Credentials(val user: String, @Secret val token: String)Complete examples
The snippets above come from these two programs. The Java file runs on its own:
// Annotations.java
import dev.elide.annotations.API;
import dev.elide.annotations.Delicate;
import dev.elide.annotations.Eager;
import dev.elide.annotations.Factory;
import dev.elide.annotations.Generated;
import dev.elide.annotations.Nonnull;
import dev.elide.annotations.Nullable;
import dev.elide.annotations.Secret;
import java.util.Arrays;
import java.util.Map;
public class Annotations {
@API
interface Greeter {
String greet(String name);
}
@Factory
static class GreeterFactory {
static Greeter create() {
return name -> "Hello, " + name;
}
}
@Eager
static class Startup {}
@Secret
record Token(String value) {}
static final Map<String, String> USERS = Map.of("ada", "Ada Lovelace");
static @Nullable String find(@Nonnull String id) {
return USERS.get(id);
}
@Delicate
static void reset() {}
@Generated
static String banner() {
return "generated";
}
public static void main(String[] args) {
System.out.println(GreeterFactory.create().greet("Ada"));
System.out.println(find("ada") + ", " + find("bob"));
reset();
for (Class<?> type : new Class<?>[] {Greeter.class, GreeterFactory.class, Startup.class, Token.class}) {
System.out.println(type.getSimpleName() + " " + Arrays.toString(type.getAnnotations()));
}
System.out.println(new Token("s3cr3t"));
}
}elide run Annotations.javaHello, Ada
Ada Lovelace, null
Greeter [@dev.elide.annotations.API()]
GreeterFactory [@dev.elide.annotations.Factory()]
Startup []
Token [@dev.elide.annotations.Secret()]
Token[value=s3cr3t]The Kotlin program is a project, because @Inject and @Singleton need jakarta.inject, which a project adds:
// elide.pkl
amends "elide:project.pkl"
name = "annotations"
jvm {
main = "MainKt"
}
dependencies {
maven {
packages { "jakarta.inject:jakarta.inject-api:2.0.1" }
}
}// src/main/kotlin/Main.kt
import dev.elide.annotations.API
import dev.elide.annotations.Delicate
import dev.elide.annotations.Eager
import dev.elide.annotations.Factory
import dev.elide.annotations.Generated
import dev.elide.annotations.Inject
import dev.elide.annotations.Nonnull
import dev.elide.annotations.Nullable
import dev.elide.annotations.Secret
import dev.elide.annotations.Singleton
@API
interface Greeter {
fun greet(name: String): String
}
@Singleton
class Polite @Inject constructor() : Greeter {
override fun greet(name: String) = "Hello, $name"
}
@Factory
object GreeterFactory {
fun create(): Greeter = Polite()
}
@Eager
class Startup
data class Credentials(val user: String, @Secret val token: String)
val users = mapOf("ada" to "Ada Lovelace")
fun find(id: @Nonnull String): @Nullable String? = users[id]
@Delicate
fun reset() {}
@Generated
fun banner() = "generated"
@OptIn(Delicate::class)
fun main() {
println(GreeterFactory.create().greet("Ada"))
println("${find("ada")}, ${find("bob")}")
reset()
for (type in listOf(Greeter::class, Polite::class, GreeterFactory::class, Startup::class)) {
println("${type.simpleName} ${type.java.annotations.filterNot { it is Metadata }}")
}
println(Credentials("ada", "s3cr3t"))
}elide runHello, Ada
Ada Lovelace, null
Greeter [@dev.elide.annotations.API()]
Polite [@jakarta.inject.Singleton()]
GreeterFactory [@dev.elide.annotations.Factory()]
Startup []
Credentials(user=ada, token=s3cr3t)