What does @Inherited do, and what are its precise limitations?
answer
- Inherited = subclass sees superclass annotation
- Classes only — not methods/fields
- extends only — NOT interfaces
- getAnnotation sees it; getDeclaredAnnotation never does
- 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
Knows @Inherited lets a subclass be seen as having a superclass's annotation.
Knows it applies to class annotations and is read via getAnnotation, and that without it annotations are not inherited.
States all the limits precisely — classes only, extends only (no interfaces), getAnnotation vs getDeclaredAnnotation — and explains the silent gotchas.
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.