1

Is it possible to attach an annotation to a new object?

Let's say we have a simple house object, House house = new House () and annotations @Large and @Spooky.

My idea was to have a switch statement so a house marked as @Spooky does something different than a house marked as @Large.

@Spooky
House house = new House();

System.out.println(house.getClass().getAnnotations().length);

Apparently there is no annotation on my new house object as length == 0.

Why is that?

Jonny Henly
  • 4,023
  • 4
  • 26
  • 43
null
  • 575
  • 4
  • 12
  • 3
    It's not `House.class` that you attach `@Spooky` to. This is just an annotated variable in your source code. – M. Prokhorov Mar 03 '17 at 17:33
  • 1
    I don't believe there's any way to get the annotations from a local variable. (And the annotations certainly aren't attached to the actual object, just the variable itself, which you can't pass around anyway.) There is _no_ way to attach arbitrary metadata to an arbitrary Java object like you seem to be trying to do. Either it's built into the class of the object, or you can't do it. – Louis Wasserman Mar 03 '17 at 17:36
  • @Abrogatum to have a `House` that might have some properties, you should have a class like `interface HouseProperty`, your House class should have `private List`, and methods `houseHas(HouseProperty property)` and `addProperty(HouseProperty p)`. After that, you can make `class Large implements HouseProperty {}` and `class Spooky implements HouseProperty`. – M. Prokhorov Mar 03 '17 at 17:37
  • Polymorphism or just have additional property in House. There's no reason to use annotation like this. And it's impossible – vadim Mar 03 '17 at 17:45
  • This is clumsy and hides crucial information in a weird place anyway. Just make `Spooky` and `Large` constructor arguments. – chrylis -cautiouslyoptimistic- Mar 03 '17 at 17:50

1 Answers1

3

In General

You can't annotate an object (that exists at runtime only). You just can annotate a declaration or, since Java 8, a type usage (that exists already at compile time).

See The Java™ Tutorials, Annotations, Annotations Basics:

Where Annotations Can Be Used

Annotations can be applied to declarations: declarations of classes, fields, methods, and other program elements. [...] As of the Java SE 8 release, annotations can also be applied to the use of types.

and The Java™ Tutorials, Annotations, Type Annotations [...]:

Before the Java SE 8 release, annotations could only be applied to declarations. As of the Java SE 8 release, annotations can also be applied to any type use. This means that annotations can be used anywhere you use a type. A few examples of where types are used are class instance creation expressions (new), casts, implements clauses, and throws clauses. This form of annotation is called a type annotation [...].

and the unfailingly ultimate source The Java® Language Specification, 9.6.4. Predefined Annotation Types:

9.6.4.1. @Target

Annotation types may be applicable in declaration contexts, where annotations apply to declarations, or in type contexts, where annotations apply to types used in declarations and expressions.

[Emphases by me.]

Anwer to your question

As @M.Prokhorov mentions in his comment to your question you're not annotating your House class but the local variable declaration of house, that is of type House.

See also this Java Annotations Tutorial:

Annotation Placement

You can place Java [declaration] annotations above classes, interfaces, methods, method parameters, fields and local variables. [...]

Creating Your Own Annotations

[...]

@Retention

[...]

@Retention(RetentionPolicy.RUNTIME)

This is what signals to the Java compiler and JVM that the annotation should be available via reflection at runtime.

[...]

RetentionPolicy.CLASS means that the annotation is stored in the .class file, but not available at runtime. This is the default retention policy, if you do not specify any retention policy at all.

[Emphases by me.]

That means you have to declare your Spooky annotation with:

@Retention( RetentionPolicy.RUNTIME )
public @interface Spooky {}

However, as @LouisWasserman mentions in his comment to your question:

I don't believe there's any way to get the annotations from a local variable.

See also the SO question ElementType.LOCAL_VARIABLE annotation type and the aforementioned JLS:

9.6.4.1. @Target

[...]

An annotation on a local variable declaration is never retained in the binary representation.

So, if you'd make house a field (a.k.a. member variable):

@Spooky
private House house = new House();

you could use:

Stream.of( this.getClass().getDeclaredFields() )
    .forEach( field -> Stream.of( field.getAnnotations() )
        .forEach( annotation -> System.out.printf( "@%s%n%s %s %s;%n",
            annotation.annotationType().getSimpleName(),
            Modifier.toString( field.getModifiers() ),
            field.getType().getSimpleName(),
            field.getName() ) ) );

or if you want to get the house field specifically:

try {
    final Field field = this.getClass().getDeclaredField( "house" );
    final Spooky spooky = field.getAnnotation( Spooky.class );
    if ( spooky != null ) {
        System.out.printf( "@%s%n%s %s %s;%n",
            spooky.annotationType().getSimpleName(),
            Modifier.toString( field.getModifiers() ),
            field.getType().getSimpleName(),
            field.getName() );
    }
}
catch ( NoSuchFieldException | SecurityException e ) {
    e.printStackTrace();
}

Output in both cases:

@Spooky
private House house;

But, as you see, even with the conciseness of streams your code doesn't become clearer with that. I'd consider the suggestions made in the comments to your questions.

Community
  • 1
  • 1
Gerold Broser
  • 14,080
  • 5
  • 48
  • 107