Java annotation: elements declared as method but value set as attribute - java

When we create a custom annotation, we declare elements as methods and later set values as if they were attributes.
For example, here we have declared a custom annotation ComponentType with elements name() and description() that look like methods.
public #interface ComponentType {
String name();// declared as method
String description();
}
When the annotation is used, they look like the below:
#ComponentType(name = "userContainer", // value looks like an attribute
description = "a user container")
public class UserEntity { }
My question is: Why doesn't Java allow to declaring elements as attributes, like this?
public #interface ComponentType {
String name; // Compilation Error
String description;
}

If the properties of an annotation weren't defined as abstract methods in an interface, they would have been members. Something like:
public #interface ComponentType {
String name;
String description;
}
However, all the members in an interface are implicitly final (and static) and the above code does not compile, because name and description aren't initialized.
But if they were actually initialized with some values:
public #interface ComponentType {
String name = "name";
String description = "description";
}
then snippets like the following one wouldn't have been possible:
#ComponentType(
name = "userContainer" //cannot assign a value to a final variable
, description = "a user container")

My observation is:
Java consider annotations as special type of interface so:
Like interface we can declare only final attributes in an annotation:
String appName = "test application";//final attribute, never reset value
Annotation may contains only abstract methods(a method that is declared without an implementation).
public #interface ComponentType {
String name();// declared as abstract method
When we annotated elements(e.g. class, method, attribute) by annotation we need to set return value of those abstract methods, which looks like attribute but actually acts as an implementation.
#ComponentType(name = "userContainer"//set return value of method name()
We can use the values we set during annotated elements(e.g. class, method, attribute) by simply calling abstract methods of annotation.
Annotation annotation = annotatedClassObject.getAnnotation(ComponentType.class);
ComponentType componentType = (ComponentType) annotation;
String someName = componentType.name(); //returns value set during annotating
So like as interface,
Annotation never support to declare any non-final attributes.
Annotation may contains some abstract methods and we need to set return value of
abstract method during annotated elements(e.g. class, method,
attribute).
Expecting More Feedback / Answer

Related

Sharing a "configuration" between annotations

I want to share a common "configuration" between multiple annotated classes. My initial approach was to point the annotations to a class which then extends the configuration class:
#MyAnnotation(config = SharedConfig.class)
class A {}
#MyAnnotation(config = SharedConfig.class)
class B {}
class SharedConfig extends BaseConfig{
public SharedConfig(){
super("abc",123)
}
}
My initial approach was to find the SharedConfig type during annotation processing and instantiate it to find out the actual config. The problem is I can't instantiate the SharedConfig class during the actual processing...
Any idea how to achieve this?
Indeed, you can't instantiate the config class. But what you can do, is query which annotations are on the config class.
So, you could imagine the following system:
old way:
#Movie(
name = "A Few Good Men",
director = #Director(lastName = "Reiner", firstName = "Rob"),
releaseYear = 1992,
liked = true)
public class Foo {}
#Movie(
name = "A Few Good Men",
director = #Director(lastName = "Reiner", firstName = "Rob"),
releaseYear = 1992,
liked = true)
public class Bar {}
new way:
#Movie(
name = "A Few Good Men",
director = #Director(lastName = "Reiner", firstName = "Rob"),
releaseYear = 1992)
public class Placeholder {
// This class serves solely as a place to store the above annotation.
private Placeholder() {}
}
#Movie(
config = Placeholder.class,
liked = true)
public class Foo {}
#Movie(
config = Placeholder.class,
liked = false)
public class Bar {}
Your rule would presumably be that anything explicitly set on the actual class is taken, and if there is nothing explicitly set but there is a 'config' class set, then the value from the annotation on that config class is taken.
Unfortunately, there's no (non-hacky) way to tell the difference between #Foo and #Foo(value="") when Foo is defined as #interface Foo {String value() default "";} - i.e. there is no way to differentiate an explicit setting of a value that is the same as the default value for a given anno parameter, so you can't actually use 'if you do not explicitly set it, then this defaulting mechanism applies' as a concept in annotations. Therefore, 'use the defaulting mechanism' must be based on the actual value - you need 'stand-in' values that mean: "Inherit from config". That means booleans are right out, unfortunately. You can use enums instead.
Here is an example:
public enum LikeStatus {
LIKED, DISLIKED, INHERIT;
}
// target classes/types
public #interface Movie {
Class<?> config() default Object.class;
LikeStatus liked default LikeStatus.INHERIT;
int releaseYear() default 0;
Director director() default #Director(lastName = "", firstName = "")
String name() default "";
}
and now you need to write some code that knows about the defaults and acts accordingly (so, if name() returns an empty string, that means you should check the config class for the Movie annotation and fetch its name. Same for a release year of 0, a director with a blank first and last name, and so on.

How to specify default mapping method for Mapstruct

I have simple object Client
public class Client {
String externalCode;
String name;
String surname;
}
And I want to map it to nearly identical object
public class User {
String internalCode;
String name;
String surname;
}
See, I want externalCode to be mapped to internalCode. And I have a method which does that. I have marker my method with my custom #CodeMapping annotation and put that annotation to qualifiedBy parameter. So, here is my mapper.
#Mapper()
ClientMapper {
#CodeMapping
String toInternalCode(String externalCode) {
return externalCode + " internal part";
}
#Mapping(target = "internalCode", source = "externalCode", qualifiedBy = CodeMapping.class)
User toUser(Client client);
}
The problem is that name and surname fields are also mapped using toInternalCode method. Mapstruct sees that I have defined a method, which maps String to String and thinks it should be used in all cases.
Is there a way to tell Mapstruct, that if no qualifiers are specified, direct mapping should be used? Or make my own method which takes string and return it and tell Mapstruct it should use that method by default?
Most likely the toInternalCode is used by all methods because the #CodeMapping annotation is not meta annotated with #Qualifier (from org.mapstruct.Qualifier).
The #CodeMapping should be defined in the following way:
import org.mapstruct.Qualifier;
#Qualifier
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.CLASS)
public #interface CodeMapping {
}

What is the method signature means? String value() default "";

I came across this method signature in Spring Component interface.
#Target({ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Indexed
public #interface Component
{
String value() default "";
}
What is the method signature String value() default ""; means?
How and When should we define such syntax for our coding purposes?
This is no method signature. it means that you can pass a String as parameter to the Component annotation, like this:
#Component(value = "value")
If you don't specify a value your self, the default value "" will be used.
If it had been like this:
String value(); // without the default
value would have been a mandatory parameter. Trying to use Component like this:
#Component()
would lead to an Exception, since you didn't provide a value.
EDIT: when to use.
If you don't know much about this syntax, or annotations in general, you shouldn't use them. About everything that can be done using annotations, especially custom made ones, can also be done without annotations.
Let's say you want to create an annotation to validate the value of a field.
I'll be using the example of Belgian postal codes. They all are 4 digits, and are between 1000 and 9999.
#Target( {ElementType.FIELD})
#Retention( RetentionPolicy.RUNTIME)
#Constraint( validatedBy = ValidatePostalCodeImpl.class)
public #interface ValidatePostalCode{
String message() default "You have entered an invalid postal code";
Class<?>[] groups() default {}; // needed for the validation
Class<? extends Payload>[] payload() default{}; // needed for the validation
int maxValue() default 9999; // so, by default, this will validate based
int minValue() default 1000; // on these values, but you will be able to
// override these
}
/* Validation implementation */
public class ValidatePostalCodeImpl implements ConstraintValidator<ValidatePostalCode, Integer> {
int upperValue;
int lowerValue;
#Override
public void initialize(ValidatePostalCode validate) {
this.upperValue = validate.maxValue(); // here you call them as if they indeed were regular methods
this.lowerValue = validate.minValue();
}
#Override
public boolean isValid(Integer integer, ConstraintValidatorContext context) {
return integer >= lowerValue && integer <= upperValue;
}
}
/* Usage */
#Entity
#Table(name = "addresses")
public class Addresses {
// home address -> In Belgium, so has to be between the default values:
#ValidatePostalCode
Integer belgianPostalCode;
// vacation home in another country, let's say the PC's there are between
// 12000 and 50000
#ValidatePostalCode(minValue = 12000, maxValue = 50000)
Integer foreignPostalCode;
}
Sure, this is a very limited example, but it should get you an idea.
The #interface keyword is used to define an annotation. This annotation has a property called value, which you could specify explicitly:
#Component(value = "myValue")
Or in the shorthand form:
#Component("myValue")
If you don't specify the value, it will default to "", as defined by the default keyword.

Annotation definition with multiple parameters

There are two parameters in the definition, value and optional.
but in the usage, there is only one parameter, so I have two questions:
If there are less parameters in usage than in definition, and the parameter name is absent, how to determine which one is used?
In the above code, value is an array type, but AuthRequired.AuthType.Server is an element, how can they match?
There is an annotation definition:
Java Code:
#Target({ElementType.METHOD, ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
public #interface AuthRequired {
enum AuthType {
Server(null),
Student(UserType.Student),
Teacher(UserType.Teacher),
SchoolMaster(UserType.SchoolMaster),
Operator(UserType.Operator);
private UserType userType;
AuthType(UserType userType) {
this.userType = userType;
}
public UserType getUserType() {
return userType;
}
}
AuthType[] value() default {};
boolean optional() default false;
}
And its usage:
Java Code:
#AuthRequired(AuthRequired.AuthType.Server)
#RequestMapping(method = RequestMethod.GET, value = "/isUserSlaveOf")
boolean isUserSlaveOf(
#RequestParam String slaveUserIds,
...
...
If you do not specify the parameter name, it's always the value parameter that's used. Also this is only legal, if you specify a single parameter. Otherwise you need to use <parameterName>=<value> for each parameter.
Instead of specifying a single element array using the {} bracket notation you are also allowed to simply write the element. #AuthRequired(value=AuthRequired.AuthType.Server) is equivalent to #AuthRequired(value={AuthRequired.AuthType.Server}).

Modify parameter value of custom annotation

Is there any way to implement annotation in order to change his parameter value by itself?
For example:
I would like create custom RequestMapping annotation to get rid of some code duplicates.
Current code:
#RequestMapping("/this/is/duplicate/few/times/some")
public class SomeController {
}
And I want to create something like this
#Retention(RetentionPolicy.RUNTIME)
#RequestMapping()
public #interface CustomRequestMapping {
String value() default "";
#AliasFor(annotation = RequestMapping.class, attribute = "value")
String "/this/is/duplicate/few/times"+value();
}
In order to reduce Request Mapping value to this:
#CustomRequestMapping("/some")
public class SomeController {
}
Unfortunately I cant find way to make that compilable.
Or maybe there is a way to use AliasFor annotation to pass parameter into destination array. Something like this:
#Retention(RetentionPolicy.RUNTIME)
#RequestMapping()
public #interface CustomRequestMapping {
#AliasFor(annotation = RequestMapping.class, attribute = "value{1}")
String value() default "";
#AliasFor(annotation = RequestMapping.class, attribute = "value{0}")
String prefixPath() default "/this/is/duplicate/few/times";
}
What it seems is you are trying to make a subtype of an annotation and modify one of its attributes default value
Subtyping of annotation is not possible and here is the JSR stating the reason.
It complicates the annotation type system,and makes it much more difficult
to write “Specific Tools”.
“Specific Tools” — Programs that query known annotation types of arbitrary
external programs. Stub generators, for example, fall into this category.
These programs will read annotated classes without loading them into the
virtual machine, but will load annotation interfaces.
One solution to your duplication problem could be to extract a constant.
#Annotation(MyClass.FOO+"localValue")
public class MyClass
{
public static final String FOO = "foo";
...
}

Categories

Resources