46

My question in short: how do I detect if a java annotation is present (and in the right place) for a given user class/object.

Details of the "problem"

Lets say I have two java classes:

public class Line {
   private List<Cell> cells;

   public Line(Object... annotatedObjects) {
     // check if annotations @Line and @Cell are present in annotatedObjects.
   }

   // getter/setter for cells.
}

public class Cell {
   // some members
   // some methods
}

A Line object holds Cells.

I also have two annotations, like:

public @interface Line {
  // some stuff here
}

public @interface Cell {
  // some stuff here
}

I also have a bunch of user classes (two will do for this example) that contain the @Line and @Cell annotations I specified, like:

@Line(name="pqr", schema="three")
public class AUserClass {
   @Cell
   private String aString;
}

@Line(name="xyz", schema="four")
public class AnotherUserClass {
   @Cell(name="birthday")
   private Date aDate;
}

The problem: When I instantiate a new Line object, I want to be able to pass the user classes/objects into the Line constructor. The Line constructor then finds out if the passed user classes/objects are valid classes that can be processed. Only user classes that have a @Line annotation for the class, and at least one @Cell annotation for its members are valid objects that can be passed into the constructor of the Line object. All other passed objects are invalid. The moment a valid user object is passed, all the available members that are tagged as @Cell in that object are transformed to Cell objects and added to the cells list.

My questions:

  1. is this possible to detect the annotations in this object/class at runtime, and only for THIS passed object (I don't want to scan for annotations on the classpath!)?
  2. is it possible to detect the datatype of the @Cell tagged members? This is needed because the Cell class doesn't accept all datatypes.
  3. is it possible to retrieve the actual member name (specified in the java file) so that the user doesn't have to specify the members Cell name. I want the user to be able to write @Cell (without a name) and @Cell(name="aName"), and when only @Cell is specified, the name of the member is used instead. I have no idea if this information is still available at runtime using reflection.
  4. How to detect if the annotations are in the right place?If code is tagged like this, then the object should be ignored (or maybe an exception is thrown)?
    @Cell // oh oh, that's no good :(
    public class WrongClass {
    // some members
    }
  5. Could you provide some startup code, so I know a little to get going with this problem. I am really new to annotations and reflection. BTW: I am using the latest jvm 1.6+

Thank you for your kind help!

Andrzej Doyle
  • 102,507
  • 33
  • 189
  • 228
user504342
  • 945
  • 2
  • 16
  • 36

2 Answers2

51

First you need to have retention policy on your annotations so you can read them with reflection

  @Retention(RetentionPolicy.RUNTIME)
  @Target(ElementType.TYPE)
  public static @interface Line {

  }

  @Retention(RetentionPolicy.RUNTIME)
  @Target(ElementType.FIELD)
  public static @interface Cell {

  }

Second you need to test if the class has the Line annotation with isAnnotationPresent(annotationClass). This method is accessible from java.lang.Class and a java.lang.reflect.Field.

NOTE: that you need to retrieve the fields that are private with class.getDeclaredField(fieldName).

3. I don't think you can make an annotation have a default value based on a propertyName but you can make name optional by providing a default String name() default DEFAULT and check for that value when iterating through the fields and either use the value stored in name() or the propertyName

Liviu T.
  • 23,584
  • 10
  • 62
  • 58
  • Answering some of my own questions: 1. see above; 2. Use the java.lang.reflect.Field methods: getName() and getType() to find out the datatype of the field; 4. see above; 5. See Thinking in Java 4th, and Java reflection in Action (good resources for answers). 3. still unanswered. – user504342 Dec 07 '10 at 00:42
  • @user504343 so you want the @Cell.name to default to the property name that it's put on? – Liviu T. Dec 07 '10 at 07:50
  • yes, that's true. If field has @Cell then pick the fieldname for the Cell.name. If field has @Cell(name="hello") then pick that as the Cell.name. If @Cell(name="") pick the fieldname too. – user504342 Dec 07 '10 at 20:12
32

Q.1 :is this possible to detect the annotations in this object/class at runtime, and only for THIS passed object (I don't want to scan for annotations on the classpath!)?

Yes it is very well possible using isAnnotationPresent

@Deprecated
public class AnnotationDetection {

    public static void main(String[] args) {
        AnnotationDetection annotationDetection = new AnnotationDetection();
        System.out.println(annotationDetection.getClass().isAnnotationPresent(Deprecated.class));
    }
}

Note that annotation which are scoped to retain at Runtime will be available only,

jmj
  • 237,923
  • 42
  • 401
  • 438
  • Can you please explain how ? – Don Srinath Jul 08 '14 at 04:22
  • 3
    @Don `this.getClass().isAnnotationPresent(SomeAnnotation.class)` – jmj Jul 08 '14 at 04:26
  • ,it will always be false. Since getClass gives instance of Class object and it doesn't contain the intended annotation. – Don Srinath Jul 08 '14 at 04:36
  • well you need to retrieve class instance of the class you want to check if that class has specific annotation present, check javadoc – jmj Jul 08 '14 at 04:37
  • yah check that, seems that it is not possible to do this at run-time. – Don Srinath Jul 08 '14 at 04:45
  • It is possible to do at runtime, let me add verbose answer – jmj Jul 08 '14 at 04:46
  • Yah, thanks for the update. Anyway there you have AnnotationDetection defined. So you know it is that class even at the compile time. What if you get a Object, which can be an instance of any class, and you want to get annotations defined for that particular passed object? – Don Srinath Jul 08 '14 at 05:07
  • I am sorry I am not understanding your comment, what is your requirement, it could be a compiled class as well in my example – jmj Jul 08 '14 at 05:09
  • My case is that say there is a method public void soutannotations(Object object), where at the compile time you don't know what the actual object that will be passed at the run time.Anyway it works too if we get declared methods and then check annotations of each of those methods. Thanks – Don Srinath Jul 08 '14 at 05:31
  • In Kotlin: `yourStringResVariable.javaClass.isAnnotationPresent(StringRes::class.java)` – Arià Apr 20 '18 at 07:47
  • @DonSrinath it would be much better if you first test what you appeals for and post it after check yourself – Nemo Nov 13 '21 at 17:50