How to read annotation which is declared over an object.
For e.g
Annotation :
AuthorInfo.java
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.FIELD)
public #interface AuthorInfo {
String author() default "Dushyant Kumar";
String login() default "dushyantashu";
}
What I am trying to do :
Book.java
#Data
public class Book {
private int serialNo;
private String review;
}
Main.java
public class Main {
#AuthorInfo (
author = "Barry Allen",
login = "theflash"
)
private static Book book = new Book();
public static void main(String[] args) {
showAnnotation(book);
}
private static void showAnnotation(Object object) {
// How to get values of annotation declared over this object.
}
}
My usecase is to generate this generic showAnnotation() method, that's why param is Object. How to achieve this? From what I explored, I only got ways to read annotation if it's declared over a class, or declared over a member of a class. Isn't there a way where given an object, if some annotation is present over it can be read?
Thanks
You can give a try with generics and reflection. Assume the Book class is annotated with AuthorInfo like below:
#AuthorInfo(author = "Ram", login = "ram")
public class Book {
}
Suppose if you want to know whether AuthorInfo is present in the object of Book, you can do like below. This is straight forward solution to know whether specific annotation is present in an object.
public class TestAnnotation {
public static void main(String[] args) {
Book book = new Book();
showAnnotation(book);
}
private static <T> void showAnnotation(T t) {
Class<? extends Object> aClass = t.getClass();
if (aClass.isAnnotationPresent(AuthorInfo.class)) {
System.out.println("AuthorInfo annotation present");
AuthorInfo authorInfo = aClass.getAnnotation(AuthorInfo.class);
System.out.println(authorInfo.author());
System.out.println(authorInfo.login());
}
}
}
Suppose, if you want to know all annotations on that object, something like below helps:
private static <T> void showAnnotation(T t) {
Class<? extends Object> aClass = t.getClass();
for (Annotation annotation : aClass.getAnnotations()) {
System.out.println(annotation.toString());
}
}
You can retrieve the object class and then explore it. Via Reflection you could get its fields and methods also, and check if any has annotations on it.
Annotations can be read using Reflection API. Like
Class<Main> clazz = Main.class;
Method[] methods = clazz.getDeclaredMethods();
Field[] fields = clazz.getDeclaredFields();
for (Method method : methods) {
Annotation[] annotation = method.getDeclaredAnnotations();
}
for (Field field : fields) {
//This will get #AuthorInfo annotation on book
Annotation[] annotation = field.getDeclaredAnnotations();
//This will get #Data annotation on Book class
Annotation[] annotationsOnFieldClass = field.getClass().getDeclaredAnnotations();
}
clazz.getDeclaredAnnotations();
Related
I'm trying to find a way to generate a POJO object from the constructor of a class tagged with the #RunWith(Parameterized.class) annotation. The POJO would be used to generate the Object[] used as inputs for the constructor. Does something like this already exist? If not how could I create it?
Example code:
#RunWith(Parameterized.class)
public class Foo {
#ParameterizedTestPOJO // Annotation that would generate POJO
public Foo(String inputString, Integer inputInteger) {
// Does something
}
#Test
public testSomething() {
// Tests something
}
#Parameterized.Parameters
public static Iterable<Object[]> generateParameters() {
List<Object[]> parameters = new ArrayList<>();
// Example of how generated POJO could be used
parameters.add(new FooParameterizedPojo("input", 1).getParameters());
return parameters;
}
}
// Generated POJO
public class FooParameterizedPojo {
private String inputString;
private Integer inputInteger;
public FooParameterizedPojo(String inputString, Integer inputInteger) {
this.inputString = inputString;
this.inputInteger = inputInteger;
}
public Object[] getParameters() {
return new Object[] {inputString, inputInteger};
}
}
Edit: Maybe I could use a custom Lombok annotation? Seems promising.
Recently, I have been intersted with the JSR-269 annotation processing and I want to write a lib to eliminate some boilerplate code with it, such as json processing. I really generate the code, then, however, I encounter a fatal problem and spent a lot of time but could not slove it.
The problem is, the RoundEnvironment.getElementsAnnotatedWith() method alway return all the elements annotated with the annotation, I cannot tistinguish which one comes from a specific class. Maybe the problem isn' t clear. I show you the code below.
#Retention(RetentionPolicy.SOURCE)
#Target(ElementType.TYPE)
public #interface JsonObject {
}
#Retention(RetentionPolicy.SOURCE)
#Target(ElementType.FIELD)
public #interface JsonField {
String value() default "";
}
#JsonObject
public class Name {
#JsonField("last") public String last;
#JsonField("first") public String first;
}
#JsonObject
public class User {
#JsonField("age") public int age;
#JsonField("name") public String name;
#JsonField("sex") public boolean sex;
}
the fisrt 2 are the annotation, JsonObject indicates that the annotated type is an JsonObject and JsonField indicates that the annotated field is an json field.
the latter 2 are the sample POJO class that I want to gennerate json-parse code.
in the Processor class, the AbstractProcessor.process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) method, when I call roundEnv.roundEnv.getElementsAnnotatedWith(JsonField.class) each type(Name, User) in a loop, the return result is all the json field, in the sample above, the result is ["last", "first", "age", "name", "sex"]. In this situatin, I cannot distinguish which field belongs to which POJO.
May be the words cannot explain what I mean. Here is what I do in the process method.
// the set[Name, User]
Set<? extends Element> jsonObjects = roundEnv.getElementsAnnotatedWith(JsonObject.class);
for (Element jsonObject : jsonObjects) {
// the set[last, first, age, name, sex], **THIS IS THE PROBLEM**
Set<? extends Element> annotatedElements = roundEnv.getElementsAnnotatedWith(JsonField.class);
// other stuff...
}
Feel free to ask me anything I didn' t mention or not clear. Any suggestion is appreciated, Thanks in advance!
You can build a collection of your json object elements by calling the getElementsAnnotatedWith(JsonField.class) method and filering the result based on the annotation of the enclosing element.
Here is a complete example (using runtime annotation processing for simplicity):
#SupportedSourceVersion(SourceVersion.RELEASE_7)
#SupportedAnnotationTypes("*")
public class ElementFilterProcessor extends AbstractProcessor {
#Retention(RetentionPolicy.RUNTIME)
public static #interface JsonObject {}
#Retention(RetentionPolicy.RUNTIME)
public static #interface JsonField { String value(); }
#JsonObject
public class Name {
#JsonField("last") public String last;
#JsonField("first") public String first;
}
#JsonObject
public class User {
#JsonField("age") public int age;
#JsonField("name") public String name;
#JsonField("sex") public boolean sex;
}
public static void main(String[] args) {
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
final JavaCompiler.CompilationTask task = compiler.getTask(
null,
null,
null,
null,
Collections.singleton(ElementFilterProcessor.class.getName()),
Collections.EMPTY_SET);
task.setProcessors(Collections.singleton(new ElementFilterProcessor()));
task.call();
}
#Override
public boolean process(
final Set<? extends TypeElement> annotations, final RoundEnvironment roundEnv) {
if (roundEnv.processingOver()) {
return true;
}
final Set<? extends Element> jsonFields =
roundEnv.getElementsAnnotatedWith(JsonField.class);
final Map<Element, List<Element>> jsonObjects = new HashMap<>();
for (final Element element : jsonFields) {
final Element classElement = element.getEnclosingElement();
if (classElement.getAnnotation(JsonObject.class) != null) {
List<Element> list = jsonObjects.get(classElement);
if (list == null) {
list = new ArrayList<>();
jsonObjects.put(classElement, list);
}
list.add(element);
}
}
System.out.println(jsonObjects);
return false;
}
}
Output:
{stackoverflow.annotation.ElementFilterProcessor.User=[age, name, sex], stackoverflow.annotation.ElementFilterProcessor.Name=[last, first]}
I'd also recommend to rather consider using a third-party library for mapping Java objects to JSON. For example, the Jackson processor
I have to #Override an abstract function and offer an array of strings (defining searchable fields for my datatable):
#Override
public String[] getQueryFields() {
return new String[] { "email", "firstname", "lastname", "lastLogin"};
}
When I look on my code I could imagine that I just reference a class and annotate the JPA column fields, e.g. by #Searchable to signal this capability and build my array:
#Column
#Getter
#Setter
...
#Searchable
private String email;
Is there a way to handle this?
You can certainly do anything that you want with Reflection. but you should try and explore for other options too. Here is a a crude example i just wrote. it will give you the fields that are annotated by #AnAnnotation.
#Target({ ElementType.METHOD, ElementType.FIELD, ElementType.TYPE })
#Retention(RetentionPolicy.RUNTIME)
public #interface AnAnnotation {
}
public class AnnotExample {
#AnAnnotation
private String b;
#AnAnnotation
private String c;
public static void getAnnotatedFields(final Class clazz) {
clazz.getAnnotation(AnAnnotation.class);
final Field[] declaredFields = clazz.getDeclaredFields();
for (final Field field : declaredFields) {
final AnAnnotation annotation2 = field.getAnnotation(AnAnnotation.class);
if (annotation2 != null) {
System.out.println(field.getName());
}
}
}
public static void main(final String[] args) {
AnnotExample.getAnnotatedFields(AnnotExample.class);
}
}
I create my annotation
public #interface MyAnnotation {
}
I put it on fields in my test object
public class TestObject {
#MyAnnotation
final private Outlook outlook;
#MyAnnotation
final private Temperature temperature;
...
}
Now I want to get list of all fields with MyAnnotation.
for(Field field : TestObject.class.getDeclaredFields())
{
if (field.isAnnotationPresent(MyAnnotation.class))
{
//do action
}
}
But seems like my block do action is never executed, and fields has no annotation as the following code returns 0.
TestObject.class.getDeclaredField("outlook").getAnnotations().length;
Is anyone can help me and tell me what i'm doing wrong?
You need to mark the annotation as being available at runtime. Add the following to your annotation code.
#Retention(RetentionPolicy.RUNTIME)
public #interface MyAnnotation {
}
/**
* #return null safe set
*/
public static Set<Field> findFields(Class<?> classs, Class<? extends Annotation> ann) {
Set<Field> set = new HashSet<>();
Class<?> c = classs;
while (c != null) {
for (Field field : c.getDeclaredFields()) {
if (field.isAnnotationPresent(ann)) {
set.add(field);
}
}
c = c.getSuperclass();
}
return set;
}
I have a class as below.
public class Emp{
private String name;
private String age;
//setters and getters
}
Have one more class below.
public class Student extends Emp{
private int marks;
//setters and getters
}
is there anyway to get the fields of a subclass using super class using java Reflection?
I need to get Student fields using Emp instance.
we can get super class fields as below:
subClass.getClass().getSuperclass().getDeclaredFields();
similarly can i get sub class fields using super class?
Is it possible?
Thanks!
I may have misunderstood your question. Do you want to do something like the following?
Emp e = new Student(...);
[do something with e]
foo = e.marks;
If yes, do it like this:
foo = ((Emp)e).marks;
However, if you want to do something like the following:
Emp e = new Emp(...);
[do something with e]
e.marks = ....
Then no, it's not possible, and I'd suspect your internal model of java's object model is either incomplete or flawed.
In theory there is a very complicated and costly way by retrieving all loaded classes and checking which of them are derived from Emp and contain the field. If the desired class wasn't loaded yet this may not help either.
Not directly, you have to write a helper method to that.
You take a class and the field name (and possibly type) as parameters, then look for that field in the given class. If you cant find it, you take the class's superclass and repeat from the beginning. You do this until you either found the field, or getSuperClass() returned null (meaning you reached the root of the inheritance tree).
This example demonstrates how to call find and call a specified method on an object. You can easily extract and adapt the logic for fields.
public static Object call(final Object instance,
final String methodName,
final Class<?>[] signature,
final Object[] args) {
try {
if (instance == null)
return null;
Class<?> instanceClass = instance.getClass();
while (instanceClass != null) {
try {
final Method method = instanceClass.getDeclaredMethod(methodName, signature);
if (!method.isAccessible())
method.setAccessible(true);
return method.invoke(instance, args);
} catch (final NoSuchMethodException e) {
// ignore
}
instanceClass = instanceClass.getSuperclass();
}
} catch (final Throwable e) {
return null;
}
return null;
}
Is it what you want? But beware of using field.setAccesible.
Parent class:
public class ParentClass {
private String parentField = "parentFieldValue";
public void printFields() throws IllegalAccessException {
Field[] fields = getClass().getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
Object fieldValue = field.get(this);
if (fieldValue instanceof String) {
String stringValue = (String) fieldValue;
System.out.println(stringValue);
}
}
}
}
Child class:
public class ChildClass extends ParentClass {
private String childField = "childFieldValue";
}
Usage:
public class Main {
public static void main(String[] args) throws IllegalAccessException {
ParentClass pc = new ParentClass();
ChildClass cc = new ChildClass();
pc.printFields();
cc.printFields();
}
}
This is the final solution!
#NonNull
public static List<Class<?>> getSubClasses() {
StackTraceElement[] trace = Thread.currentThread().getStackTrace();
String method = trace[3].getMethodName();
if (!"<init>".equals(method)) {
throw new IllegalStateException("You can only call this method from constructor!");
}
List<Class<?>> subClasses = new ArrayList<>();
for (int i = 4; i < trace.length; i++) {
method = trace[i].getMethodName();
if ("<init>".equals(method)) {
try {
subClasses.add(Class.forName(trace[i].getClassName()));
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
} else {
break;
}
}
return subClasses;
}
this are some examples of usage:
class a {
public a(){
print(getSubClasses());
}
}
class b extends a{
}
class c extends b{
}
And the result is
new a() -> []
new b() -> [b.class]
new c() -> [b.class, c.class]