How do I generate a class which takes a Person instance and returns the birthday as a String instead of a Date formatted with the value of the @MyFormat annotation, without writing that subclass by hand?
The purpose is the use the generated instances for HTML page generation
class Person {
@MyFormat("%td.%<tm.%<tY")
public Date getBirthday() { return birthday; }
}
// Usage somewhere in the code
...
List<Person> people = people.parallelStream()
.map(p -> MyFormatInterceptor.wrap(p))
.collect(toCollection(ArrayList::new));
System.out.println(people.iterator().next().getBirtday()) // 31.Mai.2015
I have this (see below).
It doesn't matter that the return type changes from Date to String because the call is made by reflection from evaluating the expression "person.birthday".
new ByteBuddy()
.subclass(person.getClass())
.method(isAnnotatedWith(MyFormat.class))
.intercept(MethodDelegation.to(MyFormatInterceptor.class))
.make()
.load(person.getClass().getClassLoader(), ClassLoadingStrategy.Default.WRAPPER)
.getLoaded();
class MyFormatInterceptor {
@RuntimeType
public static Object format(@Origin Method m, @SuperCall Callable<?> zuper) {
MyFormat formatAnnotation = m.getAnnotation(MyFormat.class);
return String.format(formatAnnotation.value(), zuper.call());
}
}
So the new class would have the same methode name "String getBirthday()" but with String as return value.
I am not entirely sure if I understand what you try to accomplish. The following code is creating a subclass of Person
which you do not use:
new ByteBuddy()
.subclass(person.getClass())
.method(isAnnotatedWith(MyFormat.class))
.intercept(MethodDelegation.to(MyFormatInterceptor.class))
.make()
.load(person.getClass().getClassLoader(),
ClassLoadingStrategy.Default.WRAPPER)
.getLoaded();
When you use this generated subclass at runtime, calling the getBirthday
method causes a ClassCastException
as the String
value cannot be cast to a Date
. Byte Buddy does not change the return type, even when invoking a method via reflection.