what will be correct Java annotation values in place of following?
#interface Demo
{
Class obj();
String class_name();
}
...
...
#Demo(obj=Class.forName("Example"),class_name=obj.getName())
i'm getting error as "annotation value must be a class literal"
Annotations must be constant at compile time. Therefore, methods are not allowed.
The above annotation can be done with this:
#Demo(obj=Example.class, class_name="Example")
Values passed to annotations must be known in compile-time, so you cannot use calls like Class.forName. Instead, you could use the class directly:
#Demo(obj = Example.class, class_name="com.exmaple.Example")
Related
public class Student{
#NotNull
private Course course= null;
#CustomValidation(enumCourse = course)
private String details = null;
}
}
How can i pass the course variable to CustomValidation annotation? Im getting an error saying that course must be an enum constant expression.
I have written a custom validation interface and validator too.
Annotation property must be constant at compile time.
You cannot use variable there.
The keyword here is cross fields validation.
You have two option:
Create annotation at class level. There you have access to all properties of class and validation should be done easy
Or create annotation at method level which return all necessary fields for validations.
#CustomAnnotations
Pair<Course, String> getCourseAndDetailForValidation() {
return Pair.of(course, details)
}
You can change return type to match your taste, it may be a List, an Array, wrapper objects...
It's specified by section 9.6.1 of the JLS. The annotation member types must be one of:
primitive
String
Class
an Enum
another Annotation
an array of any of the above
Course must be one of those types.
I have interface Resource and several classes implementing it, for example Audio, Video... Further, I have created custom annotation MyAnnotation with Class type param:
#MyAnnotation(type = Audio.class)
class Audio {
...
}
#MyAnnotation(type = Video.class)
class Video{
...
}
In some other place in code I have to use Interface Resource as a returned type:
public class Operations<T extends Resource> {
....
#OtherAnnotation(type = Audio.class (if audio), type = Video.class (if video) )
T getResource();
....
}
The question is how to appropriatelly annotate annotation #OtherAnnotation depending of what kind of Resource type will be returned ?
What you are asking is for dynamic values for annotation attributes.
However annotations can only be set at compile time which is the reason why their values can only be compile time constants. You may only read them at runtime.
There was a similar question in which someone tried to generate the annotation value , it's answer explains why there is no way to dynamically generate a value used in annotation in a bit more detail. In that question there was an attempt to use a final class variable generated with a static method.
There are annotation processors which offer a bit more flexibility by handling placeholders. However i don't think this fits your case, as you want the dynamic values at runtime.
This answer refers to spring's use of the expression language for the Value annotation in which the placeholder (#Value("#{systemProperties.dbName})") gets overrided with the data from one of the property sources defined ( example in spring boot )
In any case, you will have to rethink your architecture a bit.
I have an annotation in my java project which has some default strings in it:
public #interface MyInterface {
String message() default "Dependency for field; must be set here";
// ...
}
How can I do internationalization here? In my classes I would load the string via a ResourceManager
public class ValidationDocument {
private String message = ResourceManager.findLiteral("ValidationDocument", "default.message");
// ...
}
I can't load the ResourceManager in the annotation definition. What would be a good way to do the internationalization here?
You're right, you cannot do it, because annotations are evaluated at compile time and thus you can only use constants, or expressions that only involve constants. Information that can only by available at run-time, such as one retrieved by calling methods, even static ones, therefore cannot be assigned in annotations.
Annotations are not designed to be dynamically modified at run-time, so you will need to change your approach.
I could only suggest to do something like:
public #interface MyInterface {
String messageKey() default "myinterface.mykey";
// ...
}
Then, your code that actually references the #MyInterface annotation instance, would use the messageKey to look-up the message in the ResourceManager. Might work depending on what you're trying to achieve with it.
The CDI class BeanManager has several methods which take parameters of type Annotation or Annotation.... For example BeanManager.getBeans(...).
I would like to know how I'm supposed to pass my annotations as parameters to those methods.
I've tried BeanManager.getBeans(MyBean.class, MyAnnotation.class), but it doesn't work that way. I've seen Class.isAnnotation(), but there's nothing like Class.asAnnotation() to retrieve it as an Annotation type.
Neither BeanManager.getBeans(MyBean.class, #MyAnnotation) worked, nor did BeanManager.getBeans(MyBean.class, (Annotation) MyAnnotation.class).
How can I retrieve my annotation class as type Annotation?
There is an example in the documentation:
beanManager.getBeans(Object.class, new AnnotationLiteral<Any>() {});
Source: 16.6. The Bean interface
You need to use
getAnnotation(Class annotationClass) Returns this element's
annotation for the specified type if such an annotation is present,
else null.
Or loop through
getAnnotations() Returns all annotations present on this element.
To get the annotation.
object.getClass().getAnnotations()
javadoc
I want to provide annotations with some values generated by some methods.
I tried this so far:
public #interface MyInterface {
String aString();
}
#MyInterface(aString = MyClass.GENERIC_GENERATED_NAME)
public class MyClass {
static final String GENERIC_GENERATED_NAME = MyClass.generateName(MyClass.class);
public static final String generateName(final Class<?> c) {
return c.getClass().getName();
}
}
Thought GENERIC_GENERATED_NAME is static final, it complains that
The value for annotation attribute MyInterface.aString must be a constant expression
So how to achieve this ?
There is no way to dynamically generate a string used in an annotation. The compiler evaluates annotation metadata for RetentionPolicy.RUNTIME annotations at compile time, but GENERIC_GENERATED_NAME isn't known until runtime. And you can't use generated values for annotations that are RetentionPolicy.SOURCE because they are discarded after compile time, so those generated values would never be known.
The solution is to use an annotated method instead. Call that method (with reflection) to get the dynamic value.
From the user's perspective we'd have:
#MyInterface
public class MyClass {
#MyName
public String generateName() {
return MyClass.class.getName();
}
}
The annotation itself would be defined as
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface #MyName {
}
Implementing the lookup for both of these annotations is rather straight-forward.
// as looked up by #MyInterface
Class<?> clazz;
Method[] methods = clazz.getDeclaredMethods();
if (methods.length != 1) {
// error
}
Method method = methods[0];
if (!method.isAnnotationPresent(MyName.class)) {
// error as well
}
// This works if the class has a public empty constructor
// (otherwise, get constructor & use setAccessible(true))
Object instance = clazz.newInstance();
// the dynamic value is here:
String name = (String) method.invoke(instance);
There is no way to modify the properties of an annotation dynamically like others said. Still if you want to achieve that, there are two ways to do this.
Assign an expression to the property in the annotation and process that expression whenever you retrieve the annotation. In your case your annotation can be
#MyInterface(aString = "objectA.doSomething(args1, args2)")
When you read that, you can process the string and make the method invocation and retrieve the value. Spring does that by SPEL (Spring expression language). This is resource intensive and the cpu cycles are wasted every time we want to process the expression. If you are using spring, you can hook in a beanPostProcessor and process the expression once and store the result somewhere. (Either a global properties object or in a map which can be retrieved anywhere).
This is a hacky way of doing what we want. Java stores a private variable which maintains a map of annotations on the class/field/method. You can use reflection and get hold of that map. So while processing the annotation for the first time, we resolve the expression and find the actual value. Then we create an annotation object of the required type. We can put the newly created annotation with the actual value (which is constant) on the property of the annotation and override the actual annotation in the retrieved map.
The way jdk stores the annotation map is java version dependent and is not reliable since it is not exposed for use (it is private).
You can find a reference implementation here.
https://rationaleemotions.wordpress.com/2016/05/27/changing-annotation-values-at-runtime/
P.S: I haven't tried and tested the second method.