Setting annotated field value using reflection in generic class. (IllegalArgumentException) - java

I have been facing an issue that implies Reflection, Annotations and Generics in Java. I have a class that creates a new instance of a generic type called B. Then it will search for any Field with the MyCustomAnnotation annotation and sets its value to a determined one.
The class that does this is:
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
public class MyInstanceCreator<B> {
private final String myValue = "Hello world!";
public B createInstance(Class<B> classType) throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
B obj = classType.getConstructor().newInstance();
for(Field f: classType.getDeclaredFields()) {
if(f.isAnnotationPresent(MyCustomAnnotation.class)) {
System.out.println("Is annotated!");
updateField(obj, f);
}
}
return obj;
}
private void updateField(B instance, Field field) throws IllegalAccessException {
field.setAccessible(true);
field.set(myValue, instance);
field.setAccessible(false);
}
}
The annotation class:
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
#Retention(RetentionPolicy.RUNTIME)
public #interface MyCustomAnnotation {}
The custom type has an annotated Field of type String:
public class MyCustomType {
#MyCustomAnnotation
private String value;
public String getValue() {
return value;
}
}
Finally my main class is:
public class MyClass {
public static void main(String args[]) {
try {
MyInstanceCreator<MyCustomType> iCreator = new MyInstanceCreator<>();
MyCustomType myObj = iCreator.createInstance(MyCustomType.class);
System.out.println(myObj.getValue());
} catch(Exception e) {
e.printStackTrace();
}
}
}
The output of the program is:
Is annotated!
java.lang.IllegalArgumentException: Can not set java.lang.String field MyCustomType.value to java.lang.String
at java.base/jdk.internal.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:167)
at java.base/jdk.internal.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:171)
at java.base/jdk.internal.reflect.UnsafeFieldAccessorImpl.ensureObj(UnsafeFieldAccessorImpl.java:58)
at java.base/jdk.internal.reflect.UnsafeObjectFieldAccessorImpl.set(UnsafeObjectFieldAccessorImpl.java:75)
at java.base/java.lang.reflect.Field.set(Field.java:780)
at MyInstanceCreator.updateField(MyInstanceCreator.java:21)
at MyInstanceCreator.createInstance(MyInstanceCreator.java:13)
at MyClass.main(MyClass.java:5)
It does not make any sense to me why reflection cannot assign a java.lang.String value to a java.lang.String field as the IllegalArgumentException message says. I must be missing something but I can't seem to figure it out.
Any help is appreciated!

Here's your problem…
...
field.set(myValue, instance);
...
Here's your fix…
...
field.set(instance, myValue);
...
Here are the docs…
public void set​(Object obj, Object value)…
...
Parameters:
obj - the object whose field should be modified
value - the new value for the field of obj being modified
…

Related

How to get path of JSON object in Jackson JsonMappingException

I am using Jackson to deserialize JSON of this form:
{
"foo" : { "bar" : "baz" }
}
The jackson code might look like:
#JsonCreator
public class MyProxy {
#JsonProperty("foo") final FooProxy foo;
}
public class FooProxy {
#JsonProperty("bar") final String bar;
}
Imagine a consumer of this API creates invalid JSON like this:
{
"foo" : { "bar" : 1 }
}
However, in this case, I receive a MismatchedInputException and the error looks like this:
Cannot construct instance of MyProxy (although at least one Creator
exists): no int/Int-argument constructor/factory method to deserialize
from Number value (1)
When I inspect the MismatchedInputException, and I call ex.getPathReference() I get:
FooProxy["bar"]->java.lang.Object[0]
I would like to be able to return the path to the broken value to the user without any reference to the underlying Java classes.
"foo.bar must be an Object."
How can I return an error message with the JSON path, and remove any reference to the Java implementation?
Something like this should do it:
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.exc.MismatchedInputException;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.stream.Collectors;
import org.junit.jupiter.api.Test;
public class DeserializationErrorTest {
#Test
void testDeserializeWrongInput() throws IOException {
final ObjectMapper mapper = new ObjectMapper();
try {
mapper.readValue("{\"foo\" : { \"bar\" : \"not-int\" }}", MyProxy.class);
} catch (MismatchedInputException e) {
throw remapMismatchedInputException(e, RuntimeException.class);
}
}
private <T extends Exception> T remapMismatchedInputException(final MismatchedInputException e, Class<T> exClass) {
try {
final String fieldName =
e.getPath().stream().map(JsonMappingException.Reference::getFieldName).collect(Collectors.joining("."));
return exClass.getConstructor(String.class).newInstance(fieldName + " must be of type " + e.getTargetType().getSimpleName());
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException pE) {
throw new IllegalArgumentException("Cannot instantiate exception class " + exClass.getSimpleName());
}
}
static class MyProxy {
#JsonProperty("foo") final FooProxy foo;
#JsonCreator
public MyProxy(#JsonProperty("foo") final FooProxy pFoo) {foo = pFoo;}
}
static class FooProxy {
#JsonProperty("bar") final Integer bar;
#JsonCreator
public FooProxy(#JsonProperty("bar") final Integer pBar) {bar = pBar;}
}
}
And will result in:
java.lang.RuntimeException: foo.bar must be of type Integer

"name" field for Integer.class returns null

Class java.lang.Class has private field name. I'm trying to get value of this field for different Class instances, but when it comes to Integer.class the value returned is null. Here's code example:
import java.lang.reflect.Field;
public class Test {
public static void main(String[] args) throws Throwable {
Class<Object> objectCls = Object.class;
Class<Integer> integerCls = Integer.class;
Class<Class> classCls = Class.class;
Field nameField = classCls.getDeclaredField("name");
nameField.setAccessible(true);
System.out.println(nameField.get(objectCls));
System.out.println(nameField.get(integerCls));
System.out.println(nameField.get(classCls));
}
}
Output will be:
java.lang.Object
null
java.lang.Class
Also I tried executing with online compilers that used JDK 9 and 10 and it was fine there. So problem is when I execute this code with JDK 8.
instead of using private field through reflection You can just use integerCls.getName()
import java.lang.reflect.Field;
public class Test {
public static void main(String[] args) throws Throwable {
Class<Object> objectCls = Object.class;
Class<Integer> integerCls = Integer.class;
Class<Class> classCls = Class.class;
Field nameField = classCls.getDeclaredField("name");
nameField.setAccessible(true);
System.out.println(nameField.get(objectCls));
/*System.out.println(nameField.get(integerCls));*/
System.out.println(integerCls.getName());
System.out.println(nameField.get(classCls));
}
}

How to change value of annotation of field on runtime in Java?

I need to change (or remove whole) annotation value on runtime from one class. I got the exapmles from SO but this solution works only for class annotations, not for the field annotations. Any idea how to get this done? Thre reason for this is to make minor change in DB model definition classes to not use enum fields as inmemory db's not having this data type like MySQL does.
Here is working(partially) solution found on SO:
package annotations;
import java.lang.annotation.Annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Field;
import java.lang.reflect.Proxy;
import java.util.Map;
public class AnnotationModification2 {
public static void main(String[] args) throws Exception {
Something annotation = (Something) Foobar.class.getAnnotations()[0];
System.out.println("oldAnnotation = " + Foobar.class.getAnnotation(Something.class).someProperty());
changeAnnotationValue(annotation, "someProperty", "another value");
System.out.println("modifiedAnnotation = " + Foobar.class.getAnnotation(Something.class).someProperty());
annotation = (Something) Foobar.class.getDeclaredField("name").getAnnotations()[0];
System.out.println("oldAnnotation = " + annotation.someProperty());
changeAnnotationValue(annotation, "someProperty", "another value");
System.out.println("modifiedAnnotation = " + annotation.someProperty());
System.out.println(Foobar.class.getDeclaredField("name").getAnnotation(Something.class).someProperty());
}
/**
* Changes the annotation value for the given key of the given annotation to newValue and returns
* the previous value.
*/
#SuppressWarnings("unchecked")
public static Object changeAnnotationValue(Annotation annotation, String key, Object newValue){
Object handler = Proxy.getInvocationHandler(annotation);
Field f;
try {
f = handler.getClass().getDeclaredField("memberValues");
} catch (NoSuchFieldException | SecurityException e) {
throw new IllegalStateException(e);
}
f.setAccessible(true);
Map<String, Object> memberValues;
try {
memberValues = (Map<String, Object>) f.get(handler);
} catch (IllegalArgumentException | IllegalAccessException e) {
throw new IllegalStateException(e);
}
Object oldValue = memberValues.get(key);
if (oldValue == null || oldValue.getClass() != newValue.getClass()) {
throw new IllegalArgumentException();
}
memberValues.put(key,newValue);
return oldValue;
}
#Something(someProperty = "some value")
public static class Foobar {
#Something(someProperty = "Old field value!")
private String name;
}
#Retention(RetentionPolicy.RUNTIME)
#interface Something {
String someProperty();
}
}
Try to use "getField" method:
Something annotation = (Something) Foobar.class.getField("name").getAnnotations()[0];

Modify field annotation value dynamically

Is is possible to change field annotation values at runtime?
I can access the values, but can't find a way to change them.
Access is possible with:
Article.class.declaredFields.find {it.name="annotatedField"}.declaredAnnotations
I think it would be best to keep a reference to an Annotation object in addition to your Field (or Object), and update the Annotation reference as you change its values. This way, when the implementation of annotations in Class.java changes, your code is less likely to break.
The answer linked in the question comments is useful for dealing with annotations containing a single element, but if you have multiple elements that you need to set, here is a more general solution that makes use of a proxy:
import java.lang.annotation.Annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class Test {
public static void main(String[] args) throws Exception {
Foo foo = new Foo();
Field field = foo.getClass().getDeclaredFields()[0];
Anno anno = field.getAnnotation(Anno.class);
System.out.println(String.format("Old properties: %s, %s, %s", anno.value(), anno.bar(), anno.barr()));
Anno anno2 = (Anno) setAttrValue(anno, Anno.class, "value", "new");
System.out.println(String.format("New properties: %s, %s, %s", anno2.value(), anno2.bar(), anno2.barr()));
Anno anno3 = (Anno) setAttrValue(anno2, Anno.class, "bar", "new bar");
System.out.println(String.format("New properties: %s, %s, %s", anno3.value(), anno3.bar(), anno3.barr()));
}
public static Annotation setAttrValue(Annotation anno, Class<? extends Annotation> type, String attrName, Object newValue) throws Exception {
InvocationHandler handler = new AnnotationInvocationHandler(anno, attrName, newValue);
Annotation proxy = (Annotation) Proxy.newProxyInstance(anno.getClass().getClassLoader(), new Class[]{type}, handler);
return proxy;
}
}
class AnnotationInvocationHandler implements InvocationHandler {
private Annotation orig;
private String attrName;
private Object newValue;
public AnnotationInvocationHandler(Annotation orig, String attrName, Object newValue) throws Exception {
this.orig = orig;
this.attrName = attrName;
this.newValue = newValue;
}
#Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// "override" the return value for the property we want
if (method.getName().equals(attrName) && args == null)
return newValue;
// keep other properties and methods we want like equals() and hashCode()
else {
Class<?>[] paramTypes = toClassArray(args);
return orig.getClass().getMethod(method.getName(), paramTypes).invoke(orig, args);
}
}
private static Class<?>[] toClassArray(Object[] arr) {
if (arr == null)
return null;
Class<?>[] classArr = new Class[arr.length];
for (int i=0; i<arr.length; i++)
classArr[i] = arr[i].getClass();
return classArr;
}
}
class Foo {
#Anno(value="old", bar="bar", barr="barr")
public Object field1;
}
#Retention(RetentionPolicy.RUNTIME)
#interface Anno {
String value();
String bar();
String barr();
}
Program output:
Old properties: old, bar, barr
New properties: new, bar, barr
New properties: new, new bar, barr

Getting sub class fields using super class using reflection?

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]

Categories

Resources