Определить, может ли одна функция «переопределить» другую функцию

#java #reflection

#java #отражение

Вопрос:

Я взламываю слегка эзотерическую библиотеку, и я озадачен, возможно, самой гиперспецифичной проблемой за всю историю. Я хочу проверить, может ли использование ссылки на метод для какого-либо метода m реализовать некоторый функциональный интерфейс FI с параметром получателя или без него (т. е. если m принадлежит какому-то классу C и c instanceof C , я хочу проверить оба c::m и C::m ).

Я решил это для некоторых случаев … проверьте арность, проверьте типы аргументов, проверьте возвращаемый тип, проверьте типы исключений, разберитесь с возможными параметрами приемника, яда яда. Но это становится сложным, когда в игру вступают дженерики.

 interface TypeMatcher {

    boolean isSubtype(final Type type);

    boolean isSupertype(final Type type);

    static TypeMatcher.of(final Type type) {
        // ...
    }
}
  
 class TypeVariableMatcher implements TypeMatcher {

    private final TypeVariable<?> typeVariable;
    private TypeMatcher binding;

    TypeVariableMatcher(final TypeVariable<?> typeVariable) {
        this.typeVariable = typeVariable;
    }

    @Override public boolean isSubtype(final Type type) {
        // TODO: check bounds... how?
        return check(type, TypeMatcher::isSubtype);
    }

    @Override public boolean isSupertype(final Type type) {
        // TODO: check bounds... how?
        return check(type, TypeMatcher::isSupertype);
    }

    private boolean check(final Type type, final BiPredicate<TypeMatcher, Type> callback) {
        if (binding == null) {
            binding = TypeMatcher.of(type);
            return true;
        }
        return callback.test(binding, type);
    }

}
  

Чтобы проверить, может ли метод реализовать функциональный метод, я просто создаю TypeMatcher кеш (пусть cache Map тогда matcherFactory = type -> cache.computeIfAbsent(type, TypeMatcher::of) будет a ). Затем я просто пытаюсь сопоставить аргументы, возвращаемый тип и типы исключений в этом порядке (это не так просто из-за переменных и возможного параметра получателя и т. Д., Но Вы получаете jist). Кэш помогает обрабатывать такие интерфейсы, как java.util.BinaryOperator , но…

Как // TODO примечание s, это не проверяет параметры ограниченного типа, поэтому этот тестовый пример завершится неудачей:

 interface FI<R extends Runnable> {
    void fm(R r1, R r2);
}

interface C {
    void cannotImplementFI(String s1, String s2);
}
  

…поскольку String будет сообщаться как «супертип» R , хотя это не так. Я также не уверен, как обрабатывать ограниченные подстановочные знаки:

 interface FI {
    <T> void fm(Supplier<? extends T> s, Consumer<? super T> c);
}

interface C {
    void canImplementFI(Supplier<String> s, Consumer<Object>);
    void cannotImplementFI(Supplier<Number> s, Consumer<Integer> c);
}
  

Возможно, мой подход неверен, или, может быть, я слишком долго боролся с одной и той же проблемой, и я упускаю что-то очевидное. Любые указания на то, как действовать дальше, были бы абсолютно прекрасны!!