Определить, может ли одна функция «переопределить» другую функцию - PullRequest
3 голосов
/ 11 апреля 2019

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

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

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)). Затем я просто пытаюсь сопоставить аргументы, возвращаемый тип и типы исключений в указанном порядке (это не так просто из-за varargs и возможного параметра получателя и т. Д., Но вы получите jist). Кэш помогает обрабатывать интерфейсы, такие как java.util.BinaryOperator, но ...

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

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);
}

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...