Assume we have the following interface and implementations:
interface Matcher<T>{
boolean matches(T arg);
}
class NumberMatcher<T extends Number> implements Matcher<T>{
@Override
public boolean matches(T arg){...}
}
class StringMatcher extends Matcher<String>{
@Override
public boolean matches(String arg){ ...}
}
class CustomMatcher extends NumberMatcher<Integer> {
public boolean matches(String arg){...}
@Override
public boolean matches(Integer arg){...}
}
What I need is the type of the parameter of the matches(T) method of a given Matcher implementation.
NumberMatcher numberMatcher = new NumberMatcher<Long>();
StringMatcher stringMatcher = new StringMatcher();
CustomMatcher customMatcher = new CustomMatcher();
Matcher<Date> dateMatcher = new Matcher<Date>(){...};
getArgumentType(numberMatcher) // should return Number.class
getArgumentType(stringMatcher) // should return String.class
getArgumentType(customMatcher) // should return Integer.class
getArgumentType(dateMatcher ) // should return Object.class
Here is a implementation that works except of the CustomMatcher case, cause it fails to detect the overriden matches(..) method and returns String.class
instead of Integer.class
.
Class<?> getArgumentType(Matcher<?> matcher) {
Method[] methods = matcher.getClass().getMethods();
for (Method method : methods) {
if (isMatchesMethod(method)) {
return method.getParameterTypes()[0];
}
}
throw new NoSuchMethodError("Method 'matches(T)' not found!");
}
private boolean isMatchesMethod(Method method) {
if (!isPublic(method.getModifiers()))
return false;
if (method.getParameterCount() != 1)
return false;
return method.getName().equals("matches");
}
EDIT:
I am looking for a solution that doesn't need to specify the argument type like this:
interface Matcher<T>{
boolean matches(T arg);
Class<T> argumentType();
}
As long as you can edit the implementations, you can use a marker annotation:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ThisIsTheOne {
}
public class CustomMatcher extends NumberMatcher<Integer> {
@Override
@ThisIsTheOne
public boolean matches(Integer arg){ return true; }
public boolean matches(String arg){ return true; }
}
private static boolean isMatchesMethod(Method method) {
if (method.getAnnotation(ThisIsTheOne.class) != null) {
return true;
}
// do the same as before, so it works on non-annotated methods too
}
This will return Integer.class
for CustomMatcher
.
I don't think there's a way to retrieve this information at runtime, since Method
-s don't know where they come from. This is probably intentional, as multiple interfaces can define the same method signature.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.