skip to content

What does @Inherited do, and what are its precise limitations?

level: seniorimportance: should knowfreq 45%

answer

  1. Inherited = subclass sees superclass annotation
  2. Classes only — not methods/fields
  3. extends only — NOT interfaces
  4. getAnnotation sees it; getDeclaredAnnotation never does
  5. Spring rolls its own (MergedAnnotations) because JDK @Inherited is narrow

basics

~10 s

@Inherited makes a class-level annotation be seen on subclasses too, when you ask via reflection. It only works for annotations on classes — not on methods, fields, or interfaces.

solid answer

~40 s

@Inherited is a meta-annotation that affects annotation inheritance through the class hierarchy. By default an annotation on a superclass is not reported on its subclasses; if the annotation type is itself marked @Inherited, then reflective lookups on a subclass that lack their own copy will report the superclass's annotation. The key limitations: it applies only to annotations used on *classes* — it is ignored on methods, fields, constructors, etc. It only follows *class* extension, never interface implementation, so an @Inherited annotation on an interface does not propagate to implementors. And the effect is visible specifically through getAnnotation/getAnnotations (the 'inherited' view), whereas getDeclaredAnnotations always returns only what is declared directly on that element. Frameworks like Spring often provide their own meta-annotation/merging logic precisely because the JDK's @Inherited is this narrow.

go deeper

for a junior

Knows @Inherited lets a subclass be seen as having a superclass's annotation.

for a middle

Knows it applies to class annotations and is read via getAnnotation, and that without it annotations are not inherited.

for a senior

States all the limits precisely — classes only, extends only (no interfaces), getAnnotation vs getDeclaredAnnotation — and explains the silent gotchas.

for a principal

Explains why framework annotation engines (Spring MergedAnnotations) supersede JDK @Inherited and designs annotation hierarchies accounting for these inheritance semantics.

## Setup An **annotation** is an `@`-marker on code; a **meta-annotation** is an annotation placed on another annotation's declaration to configure it. `@Inherited` configures whether an annotation is treated as **inherited by subclasses**. ## Default: annotations are NOT inherited Normally, if class `Base` has annotation `@A` and `Sub extends Base`, asking `Sub.class.getAnnotation(A.class)` returns `null`. Annotations do not flow down the hierarchy by default. ## What @Inherited changes If the annotation type `A` is declared as `@Inherited`, then a reflective query on `Sub` that does not find `@A` directly will walk *up the superclass chain* and report the `@A` from `Base`. So `Sub.class.getAnnotation(A.class)` now returns the inherited instance. ## The four precise limitations (the interview meat) 1. **Class-targeted only.** `@Inherited` has an effect **only for annotations applied to classes**. If you put an `@Inherited` annotation on a *method* or *field*, the @Inherited has no effect — method/field annotations are never inherited regardless. (Method override inheritance is a separate, unrelated concept.) 2. **Class extension only, not interfaces.** It follows `extends` between classes. It does **not** propagate across `implements`: an `@Inherited` annotation on an interface is *not* inherited by classes that implement that interface, nor by sub-interfaces. 3. **Visible via the 'inherited' reflection view.** `getAnnotation(...)` and `getAnnotations()` honor inheritance and will show inherited annotations. `getDeclaredAnnotation(...)`/`getDeclaredAnnotations()` always return only annotations **declared directly** on the queried element — they ignore @Inherited. 4. **Nearest declaration wins.** If both `Base` and `Sub` declare `@A`, the one on `Sub` (the queried class) is what you get; inheritance only fills in when the subclass has none. ## Why it matters in practice Because @Inherited is so narrow (no interfaces, classes only, no method/field inheritance), most frameworks roll their own. Spring's `@Transactional`, `@Component` stereotypes, and meta-annotation 'merging' are implemented by Spring's `AnnotatedElementUtils`/`MergedAnnotations`, *not* by the JDK's @Inherited — which is why a Spring annotation can be inherited from an interface even though plain @Inherited never would. ## Example ```java import java.lang.annotation.*; @Inherited @Retention(RetentionPolicy.RUNTIME) @interface Role {} @Role class Base {} class Sub extends Base {} // Sub.class.getAnnotation(Role.class) -> non-null (inherited) // Sub.class.getDeclaredAnnotation(Role.class) -> null (not declared on Sub) ```

  • Why does an @Inherited annotation on an interface not appear on implementing classes?
    @Inherited only follows class-to-subclass extension, not interface implementation or sub-interfacing. The JDK deliberately limits it to the superclass chain, which is why frameworks like Spring add their own interface-aware inheritance.
  • What is the difference between getAnnotations() and getDeclaredAnnotations() with respect to @Inherited?
    getAnnotations() includes inherited annotations (honoring @Inherited up the superclass chain); getDeclaredAnnotations() returns only annotations declared directly on that element and ignores @Inherited entirely.

saying these in an interview costs you the question

  • Claiming @Inherited makes method or field annotations inherited.
  • Believing @Inherited propagates annotations from interfaces to implementers.
  • Saying getDeclaredAnnotations() returns inherited annotations.
  • Confusing Spring's meta-annotation merging with the JDK's @Inherited.

context