#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);
}
Возможно, мой подход неверен, или, может быть, я слишком долго боролся с одной и той же проблемой, и я упускаю что-то очевидное. Любые указания на то, как действовать дальше, были бы абсолютно прекрасны!!