Unable to create annotation with multiple bound generic class arguments - java

I'm trying to create an annotation which can accept multiple classes as input. Typical usage would be
#Prerequisites{FirstPrerequisite.class, SecondPrerequisite.class}
For this I can create an annotation as shown below
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
#Inherited
public #interface Prerequisites {
public Class<?>[] value();
}
I want to restrict the types that are allowed in the prerequisites annotation. If I give something like public Class<? extends Dependency>[] value(); It is working however, the problem is My FirstPrerequisite and SecondsPrerequisite may bot be extended from same type. I tried the following but none seemed to work, they are giving compilation errors.
public Class<? extends Dependency & TestCaseStep>[] value();
public Class<? extends Dependency , TestCaseStep>[] value();
public Class<T extends Dependency & TestCaseStep>[] value();
How to bound Generics to take inputs of two different types?

One option is to create a marker interface and have your classes implement the interface. You can then provide a bound with that interface type.
Another alternative is to simply move the constraint checking at runtime instead of at compile time. Have your annotation processor validate the Class arguments that were provided.

Related

Retrieve Class instance of Generic Type without taking the Generic as an input

I have a class:
public abstract class BaseDaoImpl<T extends BaseModel> implements BaseDao<T> {
}
I'm also using annotations to generate SQL queries (for various reasons I'm not able to use a framework such as hibernate) such as #Table & #Column
I would like to be able to retrieve the <T extends BaseModel> .class instance without having to take T as an input on a method.
I suppose the easy alternative would be to create a method:
public void set(Class<T> clazz){}
However I'd like to avoid this if possible to keep my code as streamlined as possible.
Is this possible?
Although using reflection is a bit of a code smell, you can get the information you need:
Class<T> modelImplementationClass = (Class<T>)
((BaseModel)this.getClass().getGenericSuperclass())
.getActualTypeArguments()[0];
Reflection is a Java API that allows you to access or modify the behavior of methods, classes, interfaces at runtime.
Reflection should generally be avoided as it's quite slow and breaks abstraction by revealing internal implementation details.
Unfortunately, it's not possible to do due to type erasure: you have to force your classes to provide meta-information in runtime. I would do something like this (an adapted snippet from a big real project).
public abstract class AbstractBaseDao<T extends BaseModel> implements BaseDao<T>{
public abstract Class<T> getType();
}
class ConcreteModel extends BaseModel {/*...*/}
class ConcreteDao extends AbstractBaseDao<ConcreteModel> {
#Override
public Class<ConcreteModel> getType() {
return ConcreteModel.class;
}
}
An alternative way is to define a custom annotation like this:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
public #interface Type {
Class value();
}
interface BaseDao<T extends BaseModel> { }
#Type(ConcreteModel.class)
public class ConcreteDao implements BaseDao<ConcreteModel> { }
...and use it in some processor for your DAOs, but this will require some additional infrastructure code to register all annotated classes. And - again - you cannot limit type bounds within annotations.

Java Validation API in sub-projects

I couldn't find anything that helped so im asking:
I have two projects. Project A is the base project that defines basic stuff. Project B is a sub-project of Project A and extends its functionality (Project A should work like a framework and there will be further Projects C and D later).
I have a domain object in Project A like:
public class BasePayload {
#CustomValidator
private String name;
}
The actual validation of the field 'name' should be in Project B, since all the sub-projects have different valid values for name.
Also i would still like to also have a validator in Project A for testing purposes.
#Target({ ElementType.FIELD })
#Retention( RetentionPolicy.RUNTIME )
#Constraint(validatedBy = AbstractFieldValidator.class)
#Documented
public #interface CustomValidator{
String message() default "message";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
}
I tried defining my own annotation (#CustomValidator) and an abstract validator that has a implementation in Project B. But it doesn't quite work since one can not instantiate the abstract class. Once i have a implementation of that abstract validator in project A, the implementation in Project B is never used.
How would one design this? Is it possible at all? Thank you for your help.

Having an annotation processor properly find classes with inherited annotations

Is it possible to force a annotation processor to RERUN after all other processors have generated their code? Basically what I'm running into right now is that Android Databinding is generating a type parameter for one of my classes causing the inherited class from its super not to propagate properly on the first pass of my processor. Any help would be much appreciated!
Thanks
Annotation:
#Inherited
#Retention(RetentionPolicy.CLASS)
#Target(ElementType.TYPE)
public #interface ContainsViewModel{
}
Example:
#ContainsViewModel
public class MyActivity extends ViewModelActivity<MyViewModel, GeneratedClass>{
//The GeneratedClass in this case is a SubClass of ViewDataBinding for the Android databinding library
#InjectHere
public void injectMethod(MyViewModel injected){
}
}
In the above example, the GeneratedClass will cause this class to never show up in
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
roundEnv.getElementsAnnotatedWith(ViewModelView.class)// does not have MyActivity
}
This seems to be because the GeneratedClass never gets generated until the last pass of my process. I can tell by doing,
roundEnv.getRootElements()
However, when I finally see the GeneratedClass show up, the roundEnv refuses to provide any elements with my ContainsViewModel annotation. Any ideas as to go about dealing with this? Thanks.

Why can #Repeatable Annotations not be inherited from interfaces

In Java annotations marked as #Inherited will only work when annotating classes:
Note that this meta-annotation type has no effect if the annotated
type is used to annotate anything other than a class. Note also that
this meta-annotation only causes annotations to be inherited from
superclasses; annotations on implemented interfaces have no effect.
So interfaces or methods annotated with an #Inherited annotation will not result in implementing classes/methods to also be annotated with the annotation. The reason for this is most likely, that the compiler would'n know which of the annotations to choose, if there are multiple annotations in the class hierarchy as described here.
Now Java 8 introduced the new annotation #Repeatable. I think it would have been natural to remove the above restrictions for annotations that are both marked as #Inherited and #Repeatable, because the compiler should then be able to add the conflicting annotations to the #Repeatable annotation.
Given the following example:
import java.lang.annotation.Inherited;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
#Inherited
#interface RepeatableAnnotations {
RepeatableAnnotation[] value();
}
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
#Inherited
#Repeatable(RepeatableAnnotations.class)
#interface RepeatableAnnotation {
String value();
}
#RepeatableAnnotation("A")
interface IntefaceA {}
#RepeatableAnnotation("B")
interface IntefaceB {}
#RepeatableAnnotation("C")
#RepeatableAnnotation("D")
public class TestClass implements IntefaceA, IntefaceB {
public static void main(String[] args) {
for (RepeatableAnnotation a : TestClass.class.getAnnotation(RepeatableAnnotations.class).value()) {
System.out.print(a.value());
}
}
}
I would have hoped the output to be ABCD but it is "just" CD (i.e. #Inherited is working exactly like pre Java 8).
Does anyone know if there was good reason for not removing the #Inherited restrictions regarding interfaces and methods in the case of #Repeatable annotations for Java 8?
Is there any workaround to achieve the ABCD output for the above type hierarchy? (other than using reflection to scan the super interfaces for annotations...)
Please recall the documentation of #Inherited:
If an Inherited meta-annotation is present on an annotation type declaration, and the user queries the annotation type on a class declaration, and the class declaration has no annotation for this type, then the class's superclass will automatically be queried for the annotation type.
In other words, #Inherited never was intended to be a feature for collecting multiple annotations on a type hierarchy. Instead, you will get the annotation of the most specific type which has an explicit annotation.
In other words, if you change your declaration to
#RepeatableAnnotation("FOO") #RepeatableAnnotation("BAR") class Base {}
#RepeatableAnnotation("C") #RepeatableAnnotation("D")
public class TestClass extends Base implements IntefaceA, IntefaceB {
it won’t change the result; FOO and BAR of Base are not inherited by TestClass as it has the explicit annotation values C and D.
Expanding this to the interface hierarchy would be awkward due to the multiple inheritance and the fact that a super-interface may turn out to be a sub-interface of another super-interface so finding the most specific one is not trivial. This differs heavily from the linear search of the superclass hierarchy.
You may encounter the situation where multiple unrelated annotated interfaces exist but it’s not clear why this ambiguity should be resolved by joining them into one repeated annotation. This would not harmonize with the behavior in all other scenarios.
Note that the answer you have linked is a bit odd as it shows code using a method annotation but method annotations are never inherited, regardless of whether you specified #Inherited or not (an audit tool should generate a warning when you combine #Target(ElementType.METHOD) with #Inherited, imho). #Inherited is relevant for type annotations only.
but I found this thread searching for a similar solution. In the end I wrote this helper method:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
public #interface Labels
{
Label[] value();
}
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
#Repeatable(Labels.class)
public #interface Label
{
String value();
}
#Label("A")
class A
{
}
#Label("B")
class B extends A
{
}
#Test
void LabelStackTest()
{
var labels = ClassUtils.getAnnotatedLabels(B.class);
assertThat(labels).contains("A");
assertThat(labels).contains("B");
}
public static List<String> getAnnotatedLabels(Class<?> labeledClass)
{
var labels = new ArrayList<String>();
do
{
labels.addAll(Arrays.asList(labeledClass.getAnnotationsByType(Label.class))
.stream()
.map(labelAnnotations -> labelAnnotations.value())
.toList());
labeledClass = labeledClass.getSuperclass();
} while (labeledClass != Object.class);
return labels;
}

Java annotations fundamentals

I am a newbie to annotations. I have gone through a lot of tutorials explaining the concept of annotations. But nowhere do i find information about defining multiple annotations within a class. So pls give me some insight on defining and accessing multiple annotations.Below is the code where I define two annotations in a class and eclipse IDE presents me an error "The public type SampleAnn must be defined in its own file".. Is the reason for this error because of the java convention that "there should only one public annotation per class in the name of the class-name"?
#Documented
#Target(ElementType.LOCAL_VARIABLE)
#Inherited
#Retention(RetentionPolicy.RUNTIME)
public #interface MethodInfo{
int number1;
}
#Documented
#Target(ElementType.LOCAL_VARIABLE)
#Retention(RetentionPolicy.RUNTIME)
public #interface SampleAnn{
int number2;
}
You are right, you can have only a single top-level class in one file.
But what you can do:
public class MyAnnotations {
public #interface SampleAnn { ... }
public #interface MethodInfo { ... }
}
There should more generally be ONE public CLASS per class file and annotations are no exception. It is also important that any publicly defined entity has the same name as its java file's name, so I don't see how you could have two in the same file.
The annotations need to be in separate compilation units (files).
The regarding top-level classes the specification states:
This restriction implies that there must be at most one such type per
compilation unit. This restriction makes it easy for a Java compiler
to find a named class within a package. In practice, many programmers
choose to put each class or interface type in its own compilation
unit, whether or not it is public or is referred to by code in other
compilation units.
Specification

Categories