Перейти до вмісту

Цю сторінку ще не перекладено, тому її показано англійською.

Elide annotations

Every annotation in dev.elide.annotations (dev.elide:elide-api) for Java and Kotlin programs: targets, retention, what each one does in Elide, and how to add the package to a project.

На цій сторінці

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.

AnnotationKotlin targetsJava targetsRetentionEffect in Elide
@APIclass, typetype, type useRuntimeNone
@Delicateclass, property, field, local variable, value parameter, constructor, function, getter, setter, typealiastype, field, method, parameter, constructor, local variableRuntimeKotlin callers must opt in
@Eagerclass, annotation classtype, annotation typeClass fileNone
@FactoryclasstypeRuntimeNone
@Generatedclass, function, file, typetype, method, type useClass fileLeft out of JVM coverage reports
@InjectAlias of jakarta.inject.InjectNot availableRuntimeNone
@Nonnullclass, typetype, type useRuntimeNone
@Nullableclass, typetype, type useRuntimeNone
@Secretproperty, classtypeRuntimeNone
@SingletonAlias of jakarta.inject.SingletonNot availableRuntimeNone

None of them take parameters. “Class file” retention means the annotation is in the bytecode but not visible to reflection.

Add to a project

ProgramElide annotations
Java file run with elide runOn the classpath
Kotlin file run with elide runOn the classpath
Kotlin source setOn the compile and run classpaths
Java source setDeclare 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.java
Hello, 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 run
Hello, Ada
Ada Lovelace, null
Greeter [@dev.elide.annotations.API()]
Polite [@jakarta.inject.Singleton()]
GreeterFactory [@dev.elide.annotations.Factory()]
Startup []
Credentials(user=ada, token=s3cr3t)

Samples

Чи була ця сторінка корисною?