简体   繁体   中英

How to find the declaring method of a bridge method?

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM