I'm stuck into trying to change the value of a #Retention(RetentionPolicy.RUNTIME) annotation with Java 17. I think my code has some problems also related to the Java 9 illegal reflective access.
That's what I've tried, but it doesn't work:
public static void changeAnnotationValue(String value) {
Greeter oldAnnotation = Greetings.class.getAnnotation(Greeter.class);
Greeter newAnnotation = new Greeter() {
#Override
public Class<? extends Annotation> annotationType() {
return oldAnnotation.annotationType();
}
#Override
public String value() {
return value;
}
};
try {
Method method = Greetings.class.getDeclaredMethod("annotationData", null);
method.setAccessible(true);
Field annotations = method.getClass().getDeclaredField("annotations");
annotations.setAccessible(true);
Map<Class<? extends Annotation>, Annotation> map = (Map<Class<? extends Annotation>, Annotation>) annotations.get(method);
map.put(Greeter.class, newAnnotation);
} catch (NoSuchMethodException | IllegalAccessException | NoSuchFieldException e) {
//Handle exceptions
e.printStackTrace();
}
}
Only found ways online for Java 8
Related
Trying to use value objects in my business model, I'm facing issues with the following code :
#Mapper
public abstract class TestMapstruct {
public ValueObjectA mapA(String value){
return new ValueObjectA(value);
}
public abstract BusinessObject map(DTO dto);
#Value
public static class ValueObjectA {
private String a;
}
#Value
public static class ValueObjectB {
private String b;
}
#Data
public static class BusinessObject {
private ValueObjectA a;
private ValueObjectB b;
}
#Data
public static class DTO {
private String a;
private String b;
}
}
Missing mapping (String -> ValueObjectB) would lead to the following compilation error message :
Can't map property "java.lang.String b" to "test.ValueObjectB b". Consider to declare/implement a mapping method: "test.ValueObjectB map(java.lang.String value)".
I totally understand this, but I would rather not to declare a method for each of my ValueObjects (could have dozens in a project).
Is there a generic way to declare (String -> ValueObject) mapping method ?
There is no generic way to do this if you don't have a common interface between all the methods.
However, if you have a common interface then it is possible. For example:
public interface ValueObject<T> {
T getValue();
void setValue(T value);
}
Then you need a helper mapper:
public interface ValueObjectMapper {
static <V extends ValueObject<T>, T> T mapToValue(V valueObject) {
return valueObject == null ? null : valueObject.getValue();
}
static <V extends ValueObject<T>, T> V mapFromValueObject(T value, #TargetType Class<V> valueObjectClass) {
if (value == null) {
return null;
}
try {
V valueObject = valueObjectClass.getDeclaredConstructor().newInstance();
valueObject.setValue(value);
return valueObject;
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
}
Edit: Add example with immutable Value objects.
In case you want your value objects to be immutable then you can do the following:
public interface ValueObject<T> {
T getValue();
}
public interface ValueObjectMapper {
static <V extends ValueObject<T>, T> T mapToValue(V valueObject) {
return valueObject == null ? null : valueObject.getValue();
}
static <V extends ValueObject<T>, T> V mapFromValueObject(T value, #TargetType Class<V> valueObjectClass) {
if (value == null) {
return null;
}
try {
V valueObject = valueObjectClass.getDeclaredConstructor(value.getClass()).newInstance(value);
return valueObject;
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
}
Note: To make this work you will have to make sure that all your ValueObjects have a constructor with that value.
My goal is to create an instance from a class that implements an interface and extends another class.
...Entity annotation:
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.METHOD, ElementType.FIELD})
public #interface Entity {
String visibileName();
}
...implementsIEventDesignDialog
public class EventDesignDialog implements IEventDesignDialog{
private String show;
private String dateAndTimeDisplayFormat;
private String eventType;
#Entity(visibileName = "Show")
public String getShow() {
return this.show;
}
#Entity(visibileName = "Date And Time display format")
public String getDateAndTimeDisplayFormat() {
return this.dateAndTimeDisplayFormat;
}
#Entity(visibileName = "Event Type")
public String getEventType() {
System.out.println("get event type method invokde successfully");
return this.eventType;
}
}
IEventDesignDialog interface:
public interface IEventDesignDialog extends IPage{
public String getShow();
public String getDateAndTimeDisplayFormat();
public String getEventType();
}
IPage interface:
public interface IPage {
}
Dynamic proxy implementation:
public class IPageProxy implements InvocationHandler {
private List<Method> entityMethods;
private Class <? extends IPage> screenClazz;
public IPageProxy(final Class <? extends IPage> screenClazz) {
entityMethods = new ArrayList<>();
getEntityAnnotatedMethods(screenClazz);
// Accept the real implementation to be proxied
this.screenClazz = screenClazz;
}
/**
* create an page instance
* #param type
* #param
* #return
* #throws InstantiationException
* #throws IllegalAccessException
*/
public static IPage getInstance(final Class<? extends IPage> type)
throws InstantiationException, IllegalAccessException {
List<Class<?>> interfaces = new ArrayList<>();
interfaces.addAll(Arrays.asList(type.getInterfaces()));
return (IPage) Proxy.newProxyInstance(
type.getClassLoader(),
findInterfaces(type),
new IPageProxy(type)
);
/*return (IPage) Proxy.newProxyInstance(type.getClassLoader(),
interfaces.toArray(new Class<?>[interfaces.size()])
, new IPageProxy(type));*/
}
/**
* get all methods that annotated with #Entity annotation
* and add it for entityMethods array List
* #param screenClazz
*/
private void getEntityAnnotatedMethods(final Class <? extends IPage> screenClazz) {
// Scan each interface method for the specific annotation
// and save each compatible method
for (final Method m : screenClazz.getDeclaredMethods()) {
if (m.isAnnotationPresent(Entity.class)) {
entityMethods.add(m);
}
}
}
static Class<?>[] findInterfaces(final Class<? extends IPage> type) {
Class<?> current = type;
do {
final Class<?>[] interfaces = current.getInterfaces();
if (interfaces.length != 0) {
return interfaces;
}
} while ((current = current.getSuperclass()) != Object.class);
throw new UnsupportedOperationException("The type does not implement any interface");
}
#Override
public Object invoke(
final Object proxy,
final Method method,
final Object[] args) throws InvocationTargetException, IllegalAccessException {
// A method on MyInterface has been called!
// Check if we need to go call it directly or if we need to
// execute something else before!
if (entityMethods.contains(method)) {
// The method exist in our to-be-proxied list
// Execute something and the call it
// ... some other things
System.out.println("Something else");
}
// Invoke original method
return method.invoke(screenClazz, args);
}
}
Main class:
public class Main {
public static void main(String[] args) {
try {
((EventDesignDialog)getInstance(EventDesignDialog.class)).getEventType();
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
}
#SuppressWarnings("unchecked")
public static <T extends IPage> T getInstance(final Class<? extends IPage> type) throws InstantiationException, IllegalAccessException {
return (T) IPageProxy.getInstance(type);
}
}
The following exception is thrown:
Exception in thread "main" java.lang.ClassCastException: com.sun.proxy.$Proxy2 cannot be cast to abc.EventDesignDialog
at abc.Main.main(Main.java:8)
You're extending Screen, which means it isn't an interface.
Dynamic Proxies work only if a base interface is present in the hierarchy.
interfaces.size() == 0
Thus the proxy can't implement any interface, and obviously it isn't part of the Screen hierarchy.
If Screen was an interface, your method is still too complex. This
public static Screen getInstance(Class<? extends Screen> type)
is sufficient.
You still receive an exception because
Class#getInterfaces
returns the interfaces which are implemented by this class.
That means if you invoke it on EventDesignDialog.class, it will return an empty array.
That means if you invoke it on EntityDesignDialog.class, still it will return an empty array.
When invoking it on Screen.class, it will return
[IPage.class]
You need to loop the hierarchy with
Class#getSuperclass
until you find a suitable interface.
A possible implementation might look like
static Class<?>[] findInterfaces(final Class<?> type) {
Class<?> current = type;
do {
final Class<?>[] interfaces = current.getInterfaces();
if (interfaces.length != 0) {
return interfaces;
}
} while ((current = current.getSuperclass()) != Object.class);
throw new UnsupportedOperationException("The type does not implement any interface");
}
which means you need to change your code to
return (IPage) Proxy.newProxyInstance(
type.getClassLoader(),
findInterfaces(type),
new IPageProxy(type)
);
But, being that you already know the result will be an IPage proxy, you can just
return (IPage) Proxy.newProxyInstance(
type.getClassLoader(),
new Class[] { IPage.class },
new IPageProxy(type)
);
Here
public static IPage getInstance(final Class<? extends IPage> type)
you're returning an IPage, but here
((EventDesignDialog)getInstance(EventDesignDialog.class))
you're trying to downcast it, which means you're trying to cast it to a more specific type. This isn't possible as the Proxy isn't of the type EventDesignDialog, but it just implements your IPage interface.
Being that Dynamic Proxies are interface-based, you'll be forced to deal with interfaces.Trying to cast to concrete classes will always throw an exception.
If you need an IEventDesignDialog, you need a new Proxy specifically for it.
How can I use a Java 8 default method in an interface to extract the Class of a Parameterized Type instead of using an abstract class?
Option 1 (Fails):
public interface EpicCoolInterface<T> {
default Class<T> getParameterizedTypeClass() {
return T.class; //doesn't work
}
Option 2 (Fails):
public interface EpicCoolInterface<T> {
default Class<T> getParameterizedTypeClass() {
return (Class<T>) ((ParameterizedType) getClass().getGenericInterfaces()[0])
.getActualTypeArguments()[0];
//java.lang.ClassCastException: java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType
}
Third attempt (successful but no interface):
public abstract class CoolAbstractClass<T> {
private Class<T> clazz;
public CoolAbstractClass() {
try {
this.clazz = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass())
.getActualTypeArguments()[0];
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public Class<T> getType() {
return clazz;
}
}
Actually, what you need is generic type inference.
Although your third attempt works, it only be applicable to specify situation(the class directly extends the abstract class).
You can use my utility class GenericUtil's method
Type[] getGenericTypes(Type sourceType, Class<?> targetClass)
You can find the source code and javadoc on github.
For your question, you can define your inteerface like this:
public static interface EpicCoolInterface<T> {
// Return Type rather than Class, because T not always be a class.
// You can do type check and return Class<T> with force typecast.
default Type getParameterizedTypeClass() {
return GenericUtil.getGenericTypes(getClass(), EpicCoolInterface.class)[0];
}
}
And let's test our code:
public static void main(String[] args) {
EpicCoolInterface<Integer> a = new EpicCoolInterface<Integer>() {
};
System.out.println(a.getParameterizedTypeClass());
EpicCoolInterface<EpicCoolInterface<Integer>> b = new EpicCoolInterface<EpicCoolInterface<Integer>>() {
};
System.out.println(b.getParameterizedTypeClass());
EpicCoolInterface<EpicCoolInterface<?>> c = new EpicCoolInterface<EpicCoolInterface<?>>() {
};
System.out.println(c.getParameterizedTypeClass());
}
it output:
class java.lang.Integer
xdean.stackoverflow.java.reflection.Q46360416.xdean.stackoverflow.java.reflection.Q46360416$EpicCoolInterface<java.lang.Integer>
xdean.stackoverflow.java.reflection.Q46360416.xdean.stackoverflow.java.reflection.Q46360416$EpicCoolInterface<?>
public <E extends Enum> E decode(java.lang.reflect.Field field, int ordinal) {
// TODO
}
Assuming field.getType().isEnum() is true, how would I produce the enum value for the given ordinal?
field.getType().getEnumConstants()[ordinal]
suffices. One line; straightforward enough.
ExampleTypeEnum value = ExampleTypeEnum.values()[ordinal]
To get what you want you need to invoke YourEnum.values()[ordinal]. You can do it with reflection like this:
public static <E extends Enum<E>> E decode(Field field, int ordinal) {
try {
Class<?> myEnum = field.getType();
Method valuesMethod = myEnum.getMethod("values");
Object arrayWithEnumValies = valuesMethod.invoke(myEnum);
return (E) Array.get(arrayWithEnumValies, ordinal);
} catch (NoSuchMethodException | SecurityException
| IllegalAccessException | IllegalArgumentException
| InvocationTargetException e) {
e.printStackTrace();
}
return null;
}
UPDATE
As #LouisWasserman pointed in his comment there is much simpler way
public static <E extends Enum<E>> E decode(Field field, int ordinal) {
return (E) field.getType().getEnumConstants()[ordinal];
}
According to title, suggest
public <E extends Enum> E decode(Class<?> enumType, int ordinal)
{
return enumType.getEnumConstants()[ordinal];
}
to be called by
YourEnum enumVal = decode(YourEnum.class, ordinal)
I am in a situation where I want to have a map where the keys are an interface class, and the corresponding value is a class which implements that interface. In other words the key and value type is related.
My current implementation of the method which adds to the map, and gets an instance of the implementation class looks like:
// should be something like Class<T>, Class<? extends T>
static Map<Class<?>, Class<?>> map = new HashMap<Class<?>, Class<?>> ();
public static <T> void add(Class<T> interfaceT,
Class<? extends T> implementationT) {
map.put(interfaceT, implementationT);
}
public static <T> T get(Class<T> interfaceT) {
// cast caused by definition not complete.
Class<T> implementationT = (Class<T>) map.get(interfaceT);
// try catch stuff omitted
T t = implementationT.newInstance();
return t;
}
My question is:
Can I define the "map" variable so the cast in the get(...) method is unneeded? I could not make the " new HashMap<Class<T>, Class<? extends T>>()' work, so either it is impossible or I missed something fundamental :)
Please advise :)
Edit: It turned out that the asSubclass() method on Class did what I wanted :D
Class<?> rawClassFromMap = map.get(interfaceT);
Class<? extends T> implementationT = rawClassFromMap.asSubclass(interfaceT);
It is fine that implementationT is of type "? extends T" as I just need a T object returned.
I like generics. Reminds me of Haskell...
It looks like the goal is something like the "Typesafe Heterogenous Container" described by Josh Bloch in Chapter 5 of Effective Java (item 29). In his case, he's mapping a type (Class<T>) to an (already-instantiated) instance (T).
You can do something similar, using asSubclass instead of cast:
final class Factory
{
private Map<Class<?>, Class<?>> map = new HashMap<Class<?>, Class<?>>();
<T> void map(Class<T> type, Class<? extends T> impl)
{
map.put(type, impl.asSubclass(type));
}
private <T> Class<? extends T> get(Class<T> type)
{
Class<?> impl = map.get(type);
if (impl == null)
throw new IllegalArgumentException("Unknown type: " + type);
return impl.asSubclass(type);
}
<T> T create(Class<T> type)
throws Exception
{
Class<? extends T> impl = get(type);
Constructor<? extends T> ctor = impl.getConstructor();
return ctor.newInstance();
}
}
I would suggest a Proxy. Here's the Java example.
public interface Bike {
public String getWheels();
public int getSize();
}
public class MountainBike implements Bike {
#Override
public int getSize() {
return 24;
}
#Override
public String getWheels() {
return "Treaded";
}
#Override
public String toString() {
String newLine = System.getProperty("line.separator");
StringBuilder sb = new StringBuilder();
sb.append("Type: MOUNTAIN").append(newLine);
sb.append("Wheels: ").append(getWheels()).append(newLine);
sb.append("Size: ").append(getSize()).append(newLine);
return sb.toString();
}
}
public class CruiserBike implements Bike {
#Override
public int getSize() {
return 26;
}
#Override
public String getWheels() {
return "Smooth";
}
#Override
public String toString() {
String newLine = System.getProperty("line.separator");
StringBuilder sb = new StringBuilder();
sb.append("Type: CRUISER").append(newLine);
sb.append("Wheels: ").append(getWheels()).append(newLine);
sb.append("Size: ").append(getSize()).append(newLine);
return sb.toString();
}
}
public class BikeProxy implements InvocationHandler {
private Object obj;
public static Object newInstance(Object obj)
{
return java.lang.reflect.Proxy.newProxyInstance(obj.getClass()
.getClassLoader(), obj.getClass().getInterfaces(),
new BikeProxy(obj));
}
public static <T> T newInstance(String className)
{
try
{
return (T) newInstance(Class.forName(className));
}
catch (ClassNotFoundException e)
{
throw new RuntimeException(e);
}
}
public static <T> T newInstance(Class<T> bikeClass)
{
try
{
return (T) java.lang.reflect.Proxy.newProxyInstance(Bike.class.getClassLoader(), new Class[]{Bike.class},
new BikeProxy(bikeClass.newInstance()));
}
catch (Exception e)
{
throw new RuntimeException(e);
}
}
private BikeProxy(Object obj)
{
this.obj = obj;
}
public Object invoke(Object proxy, Method m, Object[] args)
throws Throwable
{
Object result;
try
{
result = m.invoke(obj, args);
}
catch (InvocationTargetException e)
{
throw e.getTargetException();
}
catch (Exception e)
{
throw new RuntimeException(e);
}
return result;
}
}
public class ProxyTester
{
public static void main(String[] args)
{
Bike mountainBike = BikeProxy.newInstance(MountainBike.class);
System.out.println(mountainBike);
Bike mountainBike2 = BikeProxy.newInstance(MountainBike.class.getName());
System.out.println(mountainBike2);
Bike cruiserBike = BikeProxy.newInstance(CruiserBike.class);
System.out.println(cruiserBike);
Bike cruiserBike2 = BikeProxy.newInstance(CruiserBike.class.getName());
System.out.println(cruiserBike2);
}
}