AnnotationExplorer.AnnotationExplorer
private AnnotationExplorer() {
All major Java @ use cases: built-ins, custom annotations, meta-annotations, repeatable annotations, type-use annotations, package and record targets, decorator-style metadata, getter/setter metadata, and reflection.
scripts/run.sh lesson_5 demo
scripts/run.sh lesson_5 exercise
scripts/apply_answers.sh lesson_5
scripts/run.sh lesson_5 verify
scripts/reset_exercises.sh lesson_5
106 pages generated for Java functions in this lesson.
@ AnnotationsThis lesson explains what the @ symbol means in Java and shows the major places it appears: built-in annotations, custom annotations, meta-annotations, repeatable annotations, type-use annotations, package annotations, record component annotations, type-parameter annotations, decorator/interceptor-style annotations, getter/setter-style annotations, reflection, and @interface annotation declarations.
| Minutes | Activity |
|---|---|
| 0-15 | Run the demo and list every visible @ usage. |
| 15-35 | Read built-in annotations: @Override, @Deprecated, @SuppressWarnings, @SafeVarargs, and @FunctionalInterface. |
| 35-55 | Read custom annotations and meta-annotations: @interface, @Retention, @Target, @Documented, @Inherited, and @Repeatable. |
| 55-75 | Trace runtime reflection over classes, methods, parameters, annotated types, packages, record components, and type parameters. |
| 75-105 | Implement the annotation reflection exercise in AnnotationExplorer. |
| 105-115 | Apply answers and run the verifier. |
| 115-120 | Discuss when annotations are useful and when explicit code is clearer. |
Use workbook.md for the full two-hour learner path. It includes annotation-reading drills, implementation rounds, and edge-case checks.
Edit src/exercise/java/tutorial/lesson5/exercise/AnnotationExplorer.java.
Functions to implement:
annotationSimpleNamesendpointSummarypermissionValuesinheritedRuntimeChecktypeUseHintsOnFirstParameterdeprecatedSummarydecoratorNamehasGetterSetterMetadatapackageMarkerValuerecordComponentColumntypeParameterTagsThe lesson compiles before implementation. After applying answers, run scripts/run.sh lesson_5 verify to check reflection, repeatable annotations, inherited annotations, type-use annotations, deprecated metadata, decorator-style metadata, getter/setter-style metadata, package annotations, record component annotations, and type-parameter annotations.
@ AnnotationsThis workbook is designed to take about two hours. The goal is to understand what Java annotations are, how annotation declarations work, where annotations can be placed, and how runtime reflection can read them.
@Something attaches metadata to Java program elements.public @interface Name declares a new annotation type.@Retention and @Target control where annotation metadata exists and where it can be used.RetentionPolicy.RUNTIME.getAnnotationsByType.@Inherited applies only to class annotations, not method annotations.@Target(TYPE_USE) lets annotations appear inside types, such as a method parameter's annotated type.@Logged or @Decorates are metadata until framework code interprets them.@Getter and @Setter require an annotation processor or framework; in this repo they are intentionally metadata only.package-info.java.Run:
scripts/reset_exercises.sh lesson_5
scripts/run.sh lesson_5 demo
scripts/run.sh lesson_5 exercise
Before opening the source, write down every @ name printed or implied by the output. Group them into:
Open:
JsonHandler.javaRequestHandler.javaAnnotationUtilities.javaFind these examples:
@Override: asks the compiler to prove a method overrides or implements another method.@FunctionalInterface: asks the compiler to prove an interface has one abstract method.@SafeVarargs: documents and checks safer use of generic varargs.@SuppressWarnings: suppresses selected compiler warnings at a narrow scope.@Deprecated: marks an API as old and exposes metadata such as since and forRemoval.For each one, answer: is this used by the compiler, tools, runtime reflection, or some combination?
Open:
Route.javaTrace.javaRole.javaRoles.javaAudited.javaNonBlank.javaGetter.javaSetter.javaLogged.javaPackageMarker.javaRecordKey.javaTypeParameterRole.javaStudy the @interface syntax. Annotation methods such as String path(); are not normal methods with bodies; they declare annotation elements.
Focus on these meta-annotations:
@Retention(RetentionPolicy.RUNTIME): reflection can read it at runtime.@Retention(RetentionPolicy.CLASS): stored in class files but not visible through normal runtime reflection.@Target(ElementType.METHOD): restricts where the annotation can be used.@Documented: tells Javadoc to include the annotation in generated docs.@Inherited: subclasses can see class-level annotation metadata.@Repeatable: lets the same annotation appear more than once.@Target(PACKAGE): used through package-info.java.@Target(RECORD_COMPONENT): used on record components and read with record reflection.@Target(TYPE_PARAMETER): used on generic type parameter declarations.Open AnnotationUseDemo.java.
Trace these reflection calls:
type.getAnnotations()type.getAnnotation(Audited.class)method.getAnnotation(Route.class)type.getAnnotationsByType(Role.class)method.getParameters()[0].getAnnotatedType().getAnnotations()type.getPackage().getAnnotation(PackageMarker.class)recordType.getRecordComponents()type.getTypeParameters()Answer these questions:
@ClassOnly not visible through isAnnotationPresent at runtime?AdminController see @Audited even though it does not declare it?@Role need both Role and Roles?@Getter not generate a getter in this repo?@Decorates to actually wrap a method call?Edit src/exercise/java/tutorial/lesson5/exercise/AnnotationExplorer.java.
Round 1:
annotationSimpleNames.type.getAnnotations().Round 2:
endpointSummary.@Endpoint.<missing> when either the method or annotation is absent.Round 3:
permissionValues.getAnnotationsByType(Permission.class), not only getAnnotation(Permissions.class).Round 4:
inheritedRuntimeCheck.UserService inherits @RuntimeCheck from BaseService.Round 5:
typeUseHintsOnFirstParameter.parameter.getAnnotatedType().getAnnotations().Round 6:
deprecatedSummary.@Deprecated.since() and @Deprecated.forRemoval().Round 7:
decoratorName.@Decorates from a method.Round 8:
hasGetterSetterMetadata.@AutoGetter on the class and @AutoSetter on the field.Round 9:
packageMarkerValue.package-info.java.Round 10:
recordComponentColumn.recordType.getRecordComponents().Round 11:
typeParameterTags.type.getTypeParameters().Run:
scripts/apply_answers.sh lesson_5
scripts/run.sh lesson_5 verify
scripts/reset_exercises.sh lesson_5
The verifier checks missing methods, missing annotations, repeatable annotations, inherited annotations, type-use annotations, non-deprecated methods, decorator metadata, getter/setter metadata, package annotations, record component annotations, and type-parameter annotations.
Design one annotation for a real project you know. Decide:
@ AnnotationsTeach annotations as metadata attached to Java declarations and types. Learners should understand common built-in annotations, how custom annotations are declared, how meta-annotations constrain them, how frameworks use reflection to read them, and why getter/setter annotations usually need annotation processors.
By the end of 120 minutes, learners should be able to:
@interface.@Retention, @Target, @Inherited, and @Repeatable.Run:
scripts/reset_exercises.sh lesson_5
scripts/run.sh lesson_5 demo
scripts/run.sh lesson_5 exercise
Know the key files:
src/demo/java/tutorial/lesson5/demo/AnnotationUseDemo.javasrc/demo/java/tutorial/lesson5/demo/AnnotationUtilities.javasrc/demo/java/tutorial/lesson5/demo/Route.javasrc/demo/java/tutorial/lesson5/demo/Role.javasrc/demo/java/tutorial/lesson5/demo/Logged.javasrc/demo/java/tutorial/lesson5/demo/Getter.javasrc/demo/java/tutorial/lesson5/demo/Setter.javasrc/exercise/java/tutorial/lesson5/exercise/AnnotationExplorer.java| Time | Segment | Instructor Moves |
|---|---|---|
| 0-15 | Demo run | Run the demo, then ask learners to classify every @ they saw. |
| 15-35 | Built-ins | Explain annotations checked by the compiler versus annotations read at runtime. |
| 35-55 | Annotation declarations | Open @interface files and explain annotation elements and meta-annotations. |
| 55-75 | Reflection | Trace how class, method, repeatable, inherited, and type-use annotations are read. |
| 75-105 | Exercise | Learners implement AnnotationExplorer in staged rounds. |
| 105-115 | Verify | Apply answers and run scripts/run.sh lesson_5 verify. |
| 115-120 | Design discussion | Ask when annotations are useful and when explicit code is better. |
Use this framing:
"The @ symbol does not run code by itself. It attaches metadata. Something else must care: the compiler, Javadoc, a framework, an annotation processor, or runtime reflection."
Important distinctions:
@Override is useful even though runtime code does not read it.@Deprecated is visible to tools and can also be reflected.@Retention determines whether runtime reflection can see an annotation.@Target prevents annotations from being placed on the wrong program element.@Repeatable changes how multiple copies are stored and retrieved.@Inherited is narrow: it only works for class annotations through class inheritance.@Logged are metadata; an interceptor or proxy must read them to add behavior.@Getter and @Setter need an annotation processor to generate methods. In this repo they are metadata only, so explicit methods still exist.Learners edit AnnotationExplorer.java.
Give hints in this order:
getAnnotations() returns runtime-visible annotations, including inherited class annotations.getAnnotationsByType.<missing> in this exercise.getAnnotatedType, not plain getAnnotations on the parameter.@Deprecated has attributes; it is not only a marker.package-info.java.RecordComponent.Class.getTypeParameters.getDeclaredAnnotations() and missing inherited annotations.getAnnotationsByType.Parameter.getAnnotations().private AnnotationExplorer() {
public static List<String> annotationSimpleNames(Class<?> type) {
public static String endpointSummary(Class<?> type, String methodName) {
public static List<String> permissionValues(Class<?> type) {
public static Optional<String> inheritedRuntimeCheck(Class<?> type) {
public static List<String> typeUseHintsOnFirstParameter(Class<?> type, String methodName) {
public static String deprecatedSummary(Method method) {
public static Optional<String> decoratorName(Class<?> type, String methodName) {
public static boolean hasGetterSetterMetadata(Class<?> type, String fieldName) {
public static Optional<String> packageMarkerValue(Package targetPackage) {
public static Optional<String> recordComponentColumn(Class<?> recordType, String componentName) {
public static List<String> typeParameterTags(Class<?> type) {
private static Optional<Method> findMethod(Class<?> type, String methodName) {
private static List<String> typeHintValues(Parameter parameter) {
public Account(String displayName) {
public String displayName() {
public void changeDisplayName(String displayName) {
private AnnotationUseDemo() {
public static void main(String[] args) throws ReflectiveOperationException {
private static String runHandler(RequestHandler handler, String name) {
private static void printClassAnnotations(Class<?> type) {
private static void printInheritedAnnotation() {
private static void printMethodAnnotations(Method method) {
private static void printRepeatableAnnotations(Class<?> type) {
private static void printRetentionVisibility() {
private static void printTypeUseAnnotation() throws ReflectiveOperationException {
private static void printDecoratorStyleAnnotation() throws ReflectiveOperationException {
private static void printGetterSetterStyleAnnotations() throws ReflectiveOperationException {
private static void printPackageAnnotation() {
private static void printRecordComponentAnnotation() {
private static void printTypeParameterAnnotation() {
private static void printConstructorProperties() throws ReflectiveOperationException {
private static void printSourceOnlyMarkers() {
private AnnotationUtilities() {
public static <T> List<T> flatten(List<T>... lists) {
public static List<String> rawListDemo() {
public static String oldEndpoint() {
public String createUser(@NonBlank String name) {
String value();
public BeanPoint(int x, int y) {
public int x() {
public int y() {
public String charge(String accountId, int cents) {
public String handle(String name) {
String value();
private NativeConstantExample() {
String value();
String value();
String handle(String name);
private RetentionExamples() {
String value();
Role[] value();
String method();
String path();
public SerializableToken(String value) {
private Object readResolve() {
public String value() {
public TypedRepository(Class<T> entityType) {
public Class<T> entityType() {
String value();
private AnnotationExplorer() {
public static List<String> annotationSimpleNames(Class<?> type) {
public static String endpointSummary(Class<?> type, String methodName) {
public static List<String> permissionValues(Class<?> type) {
public static Optional<String> inheritedRuntimeCheck(Class<?> type) {
public static List<String> typeUseHintsOnFirstParameter(Class<?> type, String methodName) {
public static String deprecatedSummary(Method method) {
public static Optional<String> decoratorName(Class<?> type, String methodName) {
public static boolean hasGetterSetterMetadata(Class<?> type, String fieldName) {
public static Optional<String> packageMarkerValue(Package targetPackage) {
public static Optional<String> recordComponentColumn(Class<?> recordType, String componentName) {
public static List<String> typeParameterTags(Class<?> type) {
private AnswerVerifier() {
public static void main(String[] args) throws NoSuchMethodException {
private static void expectEquals(Object expected, Object actual, String label) {
public String save(String value) {
String value();
String method();
String path();
private ExerciseRunner() {
public static void main(String[] args) throws NoSuchMethodException {
private LegacyApi() {
public static String oldName() {
String value();
String value();
Permission[] value();
public Profile(String nickname) {
public String nickname() {
public void changeNickname(String nickname) {
String value();
String value();
String value();
String value();
public String findUser(@TypeHint("external-id") String id) {
private AnnotationExplorer() {
public static List<String> annotationSimpleNames(Class<?> type) {
public static String endpointSummary(Class<?> type, String methodName) {
public static List<String> permissionValues(Class<?> type) {
public static Optional<String> inheritedRuntimeCheck(Class<?> type) {
public static List<String> typeUseHintsOnFirstParameter(Class<?> type, String methodName) {
public static String deprecatedSummary(Method method) {
public static Optional<String> decoratorName(Class<?> type, String methodName) {
public static boolean hasGetterSetterMetadata(Class<?> type, String fieldName) {
public static Optional<String> packageMarkerValue(Package targetPackage) {
public static Optional<String> recordComponentColumn(Class<?> recordType, String componentName) {
public static List<String> typeParameterTags(Class<?> type) {