Zum Inhalt springen
Anjunar/ DOCS

Befehle per Annotation

Eine Befehlstabelle aus annotierten Methoden: finden, ihre Parameter für Hilfetexte beschreiben und per Namen aufrufen. Das Muster hinter Routern, CLI-Werkzeugen und RPC-Endpunkten.

Modell

Methoden, die einen Namen tragen

Eine Methodenannotation mit einem Wert. Die Klasse bleibt eine gewöhnliche Klasse, die du ohne Befehlstabelle testen kannst.

java
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface Command { String value(); }
scala
class Shell { @Command("greet") def greet(name: String): String = s"Hello $name" @Command("shout") def shout(text: String): String = text.toUpperCase def helper(): Unit = () }
Tabelle

Von Methoden zu einer Map

methods enthält geerbte und überschriebene Methoden genau einmal, und findAnnotation sieht auch Annotationen an einer Interface-Methode.

scala
val shell = TypeResolver.resolve(classOf[Shell]) val commands = shell.methods.flatMap { method => Option(method.findAnnotation(classOf[Command])).map(command => command.value() -> method) }.toMap commands.toSeq.sortBy(_._1).foreach { case (name, method) => println(s"$name(${method.parameters.map(p => s"${p.name}: ${p.parameterType.name}").mkString(", ")})") }
Ausgabe
greet(name: String) shout(text: String)
Aufruf

Per Namen, mit geprüften Argumenten

Prüfe die Anzahl der Argumente gegen parameters, bevor du aufrufst; die Parametertypen sagen dir, wie Texteingaben umzuwandeln sind.

scala
def run(target: Shell, name: String, args: String*): Any = val method = commands.getOrElse(name, sys.error(s"Unknown command $name")) require(method.parameters.length == args.length, s"$name takes ${method.parameters.length} arguments") method.invoke(target, args*) println(run(new Shell, "greet", "Ada")) // Hello Ada
Die Tabelle einmal bauen

Auflösen und Durchsuchen passieren einmal; danach ist jeder Aufruf ein Map-Zugriff und ein invoke.