I noticed that if I create an annotation:
public #interface NullableTypeOverride {
NullableType hibernateTypeOverride();
}
I have limited options for annotation attributes. The above code will not work because annotations only take primitive, String or Class types for their attributes.
So in this case I can't use this annotation like this:
#NullableTypeOverride(hibernateTypeOverride = Hibernate.INTEGER)
private Long distance;
My guess is that it has something to do with compile time vs. runtime but I'm not entirely sure. So what is the reason for this limitation and how can I work around it?
The JLS states
It is a compile-time error if the return type of a method declared in
an annotation type is not one of the following: a primitive type,
String, Class, any parameterized invocation of Class, an enum type
(§8.9), an annotation type, or an array type (§10) whose element type
is one of the preceding types.
The reason for this is that annotations must have a constant value. If you provide a reference to an object that may change, you'll have problems. This is only relevant if the annotation's Retention is RUNTIME.
public class Person {
public String name;
}
#Retention(RetentionPolicy.RUNTIME)
public #interface MyAnnotation {
Person person();
}
#MyAnnotation(person = ???) // how to guarantee it won't change at runtime?
public void method1() {...}
What's that value supposed to be? And how can reflection libs cache it?
MyAnnotation annotation = method.getAnnotation(MyAnnotation.class);
annotation.person(); // should be the same value every time
Remember, annotations are supposed to be metadata.
Related
This question already has answers here:
Java Annotations values provided in dynamic manner
(3 answers)
Modify a class definition's annotation string parameter at runtime
(7 answers)
Closed 3 years ago.
I have this annotated class in one of my projects.
The annotation class is defined by me.
Question: can I instead of the String "TP_REPORT" use some property
whose value is not known at compile time but only at runtime?
#PhoenixTable(tableName="TP_REPORT")
public class TPData {
...
}
Just as a reference, here is the definition of my annotation type.
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
public #interface PhoenixTable {
public String tableName();
}
No. It's not possible in Java. Values in an annotation should be constant at compile time.
You cannot give any object reference since you don't have any object when declaring an annotation. From docs by Oracle (click here):
Return types are restricted to primitives, String, Class, enums,
annotations, and arrays of the preceding types.
But if you absolutely need something dynamic, you can give a class and use that class to read from some property file. Although this may not be the best solution for you, it may give you something to investigate.
public interface TableNameProp{
public static String getTableName(){
return "TP_REPORT"; //or read from some prop and use it
}
}
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
public #interface PhoenixTable {
public Class<TableNameProp> tableProps();
}
#PhoenixTable(tableProps=TableNameProp.class)
public class TPData {
...
}
[ANSWER EDIT]: Short answer is that what I'm looking to do isn't possible. My question is a little misleading. I learnt that the Marker Interface pattern is actually what I called the Marked Annotations in my question (since the annotation you're creating is actually an interface). And checks on that can only be made at runtime. So if you're looking to make a compile time check with annotations well it's just not possible. An empty interface is the only option. Check answer to see how to do it at runtime.
I'm trying to avoid using marker interfaces in favor of marked annotations. Basically I want a bunch of classes be marked with this annotation, and pass instances of those classes to methods that accept that type. Here is my code:
MARKER ANNOTATION:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
public #interface Message {
}
CLASS:
#Message
public class MessageTypeA {
}
METHOD:
public class DatabaseWriter {
public void save(Message msg) {
//some code
}
}
CALLING CODE:
MessageTypeA msgA = new MessageTypeA();
DatabaseWriter writer = new DatabaseWriter();
writer.save(msgA);
However I get Error:(78, 23) java: incompatible types: MessageTypeA cannot be converted to Message
I'm not sure if what I'm doing is possible, but I read that marker interfaces can be replaced with marker annotations. Is it not possible in this case?
Thanks
The marker interface pattern is a way of adding metadata to your program types or obbjects that is readable in runtime.
See for example hibernate implementation of this pattern. Their insert method accepts a plain java.lang.Object, and is inside that method where the metadata from the various annotations is used.
So, following your example implementation, I'd go with something like this
public class DatabaseWriter {
public void save(Object msg) {
if (msg.getClass().isAnnotationPresent(Message.class)) {
//some code
}
}
}
In your example, MessageTypeA and Message are unrelated in the class hierarchy. A method call is legal only if the expression's type is a subtype of the formal parameter's type.
One way to establish a subtyping relationship is with an interface, as you already noted.
Another way to establish a subtyping relationship is with type qualifiers (expressed as type annotations).
TYPE QUALIFIER HIERARCHY:
#Message
|
#MessageTypeA
where #MessageTypeA is a subtype of #Message, and #Message means an unknown type of message. #Message is the default if no type annotation is written.
LIBRARY
public class DatabaseWriter {
public void save(Object msg) {
// some code that can run on any old message
}
public void saveA(#MessageTypeA Object msg) {
// some code that is specific to MessageTypeA
}
}
CLIENT
Object msg = ...;
#MessageTypeA Object msgA = ...;
DatabaseWriter writer = new DatabaseWriter();
writer.save(msg); // legal
writer.save(msgA); // legal
writer.saveA(msg); // compile-time error
writer.save(msgA); // legal
There is no run-time overhead or representation: the enforcement is done at compile time.
A tool that enables you to build pluggable type-checkers that enforce correct usage is the Checker Framework. (Disclaimer: I am a maintainer of the tool, but it is a regular part of the development toolchain at Amazon, Google, Uber, etc.)
You can define your own type system in a few lines of code. However, still consider using Java subtypes rather than type qualifiers.
How does annotation work with Java? And how can I create custom annotations like this:
#Entity(keyspace=':')
class Student
{
#Id
#Attribute(value="uid")
Long Id;
#Attribute(value="fname")
String firstname;
#Attribute(value="sname")
String surname;
// Getters and setters
}
Basically, what I need to have is this POJO be serialized like this when persisted:
dao.persist(new Student(0, "john", "smith"));
dao.persist(new Student(1, "katy", "perry"));
Such that, the actual generated/persisted object is a Map<String,String> like this:
uid:0:fname -> john
uid:0:sname -> smith
uid:1:fname -> katy
uid:1:sname -> perry
Any ideas how to implement this?
If you create custom annotations you will have to use Reflection API Example Here to process them.
You can refere How to declare annotation.
Here is how example annotation declaration in java looks like.
import java.lang.annotation.*;
/**
* Indicates that the annotated method is a test method.
* This annotation should be used only on parameterless static methods.
*/
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface Test { }
Retention and Target are known as meta-annotations.
RetentionPolicy.RUNTIME indicates that you want to retain the annotation at runtime and you can access it at runtime.
ElementType.METHOD indicates that you can declare annotation only on methods similarly you can configure your annotation for class level, member variable level etc.
Each Reflection class has methods to get annotations which are declared.
public <T extends Annotation> T getAnnotation(Class<T> annotationClass)
Returns this element's annotation for the specified type if such an annotation is present, else null.
public Annotation[] getDeclaredAnnotations()
Returns all annotations that are directly present on this element. Unlike the other methods in this interface, this method ignores inherited annotations. (Returns an array of length zero if no annotations are directly present on this element.) The caller of this method is free to modify the returned array; it will have no effect on the arrays returned to other callers.
You will find these methods present for Field, Method,Class classes.
e.g.To retrieve annotations present on specified class at run time
Annotation[] annos = ob.getClass().getAnnotations();
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:
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!)?
is it possible to detect the datatype of the #Cell tagged members? This is needed because the Cell class doesn't accept all datatypes.
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.
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
}
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!
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
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,
I jave a simple Java bean with 4 attributes, getter/setter, and some overided methods like toString, equals and hashCode.
Above every Attribute is a custom Annotation:
import java.lang.annotation.*;
import java.lang.annotation.RetentionPolicy;
#Target(ElementType.FIELD)
#Retention( RetentionPolicy.RUNTIME )
public #interface DAOProperty {
String name();
String type();
boolean identifier() default false;
}
/** The id. */
#DAOProperty(name = "id", type = "long", identifier = true)
private long id;
If I pass the bean.class to another method
generateEntity(User.class);
...
private static MEntity generateEntity(Class<?> bean) {...}
and debug it, it seems to bee empty, except for the class name. All arrays like methods, annotations and fields are of zero size.
Where did I go wrong?
Use beanClass.getDeclaredFields() instead of getFields(). Then iterate the array and for each Field call getAnnotations()
getFields() (and the similar methods) return only the public members.
Anyway, why don't you use JPA, instead of creating your own annotations and annotation processors?
Don't look at the internal fields of the class. They aren't relevant for you. The only thing that should interest you are the return values of the methods. It's very likely that java.lang.Class uses those fields to store information that is created on-demand.
In that case looking at the fields of the Class object at runtime won't tell you the right values (at least not always). Inspecting the return values of the desired methods, however should give the right values.