EMZETT.
Login

Annotations

In short: Metadata attached directly in the code to a class, method, or variable, without changing its actual behaviour — a kind of structured comment that tools and the runtime environment can also read.

In more detail: Annotations are used, for example, to mark an overridden method (so the compiler warns about a typo in the method name), to flag test methods, or to tell frameworks how a class should be automatically processed (e.g. when converting to JSON). Unlike normal comments, annotations can be actively evaluated by compilers, frameworks, and tools.

In Depth

The decisive difference from an ordinary comment: a comment is completely ignored when compiling, while an annotation (depending on configuration) survives into the compiled code or even to runtime and can be read there via reflection. This makes annotations the basis for many modern frameworks that control behaviour “declaratively” instead of through explicit code.

Typical use cases:

@Override           // "this method definitely overrides an inherited one" -> compiler error instead of a silent bug
@Deprecated          // "don't use anymore, may be removed" -> IDE shows a warning
@Test                // test framework recognises: run this method automatically
@JsonProperty("id")  // serialisation framework: this field is named differently in JSON

There are roughly three levels of effect:

  1. Tools/IDE only (e.g. @Deprecated) — affects neither the compiler nor the runtime directly, only the development environment warns.
  2. For the compiler (e.g. @Override) — the compiler checks the annotation and stops with an error on a violation.
  3. Readable at runtime — frameworks like dependency-injection or ORM systems read annotations via reflection and change their behaviour accordingly, without the code itself asking for it at all.

The advantage over pure configuration code: the metadata sits directly next to what it refers to, instead of in a separate configuration file that easily goes stale or falls out of sight (“configuration close to the code”). The downside: too many annotations make a class cluttered and blur the line between actual logic and framework configuration — critics then speak of “annotation hell”.

Historical context

Annotations were introduced explicitly in many languages (Java, for example, with version 5, 2004) as a reaction to a previous generation of frameworks that controlled behaviour almost exclusively via extensive external XML configuration files. These XML files were separate from the actual code, grew to hundreds of lines in larger projects, and were hard to refactor, because IDE tools could less reliably follow references in XML than references in the code itself. Annotations move the same information back to the spot it refers to — a shift from “configuration over convention” to a more code-centric style.

Defining custom annotations

Annotations aren’t limited to the built-in language annotations — frameworks and even individual projects can define their own, to attach project-specific metadata (e.g. a marker for which API endpoints require authentication, or which fields should be hidden when logging):

// Define a custom annotation (Java-like pseudo-syntax)
annotation RequiresAuthentication { }
 
@RequiresAuthentication
method deleteAccount() { ... }
 
// Read at runtime via reflection and react to it
if method.hasAnnotation(RequiresAuthentication):
    checkSession()

Not every language calls the same concept “annotation” — Python has a very similar tool with decorators, syntactically marked with @, but technically a function that wraps another function and changes or extends its behaviour:

@cache            # Python decorator: automatically cache the result
@login_required   # framework decorator: only allow the call with a valid session
def show_profile(user):
    ...

While classic annotations (Java, C#) are pure metadata that has to be interpreted by the framework, Python decorators ARE themselves executable code — they actually wrap the decorated function into a new function. The basic principle (marking metadata/behaviour directly on the code instead of configuring it separately), though, is the same.

See also: Classes, Methods