Reputation: 12670
I am using reflection proxies to perform additional checking on a public API. Essentially I want to wrap every object that comes back from it so that any object the caller gets their hands on is a proxy to the real object.
Java still has the whole erasure problem, so I am passing the type of the wrapped object around with it. I should know what type everything is because the entry into the API is a single, non-generic interface.
public class ProxyInvocationHandler implements InvocationHandler {
private final Object delegate;
private final Type delegateType;
public ProxyInvocationHandler(Object delegate, Type delegateType) {
this.delegate = delegate;
this.delegateType = delegateType;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) {
// Omitted: additional checks performed here.
Object result = method.invoke(delegate, args);
Type returnType = method.getGenericReturnType();
// e.g. if delegateType is List<Cat> and the method is the get method,
// returnType would be E but resultType should be Cat.
Type resultType = ???
// Utility method I will omit, it just creates another proxy instance
// using its own invocation handler.
return ProxyUtils.wrap(result, resultType);
}
}
I have looked around the Type / ParametrizedType API and can't seem to find a way to get resultType
, even though delegateType
and returnType
should be enough information to compute this.
What is the "proper" way to do this?
Upvotes: 1
Views: 1338
Reputation: 12670
Here's the Guava way, for people who come by in the future:
...
Type returnType = method.getGenericReturnType();
TypeToken<?> resultType = TypeToken.of(delegateType).resolveType(returnType);
I changed the type of delegateType to TypeToken<?>
to make things a little easier. And of course, I added quite a bit of caching (using a LoadingCache
) to get the performance down to a sensible speed. Their resolution code was slower than my initial hacked-up code which did the same thing. The difference is that I now have the confidence that it's being done correctly.
Upvotes: 1
Reputation: 4571
You can use Java ClassMate for that purpose. You'll have to use com.fasterxml.classmate.GenericType
for type tokens:
GenericType<?> delegateType = new GenericType<List<Cat>>() {};
Note the empty {}
that's called the "Super-type Token" pattern.
TypeResolver typeResolver = new TypeResolver();
MemberResolver memberResolver = new MemberResolver(
ResolvedType type = typeResolver.resolve(delegateType);
ResolvedTypeWithMembers members = memberResolver.resolve(type, null, null);
ResolvedMethod[] methods = members.getMemberMethods();
Cache the results in a Map
:
Map<Method, ResolvedMethod> resolved = new HashMap<>();
for (ResolvedMethod method: methods) {
resolved.put(method.getRawMember(), method);
}
Now, when you have a method declared by the delegateType
, i.e. List
, you can get its resolved return type:
Method method = List.class.getMethod("get", int.class);
ResolvedType resultType = resolved.get(method).getReturnType();
System.out.println("resultType = " + resultType); // prints resultType = Cat
Upvotes: 1