Проверка типа возвращаемого значения отраженного метода и параметров в Java
-
03-07-2019 - |
Вопрос
У меня есть общий объект обратного вызова, который обеспечивает (примитивную) возможность обратного вызова для Java при отсутствии замыканий.Объект обратного вызова содержит метод и возвращает параметры и типы возвращаемых значений для метода через пару методов доступа, которые просто делегируют эквивалентные методы в методе.
Я пытаюсь проверить, что предоставленный мне обратный вызов указывает на действительный метод.Мне нужно, чтобы назначение типа возвращаемого значения было совместимо с Number, а все параметры были совместимы с назначением Double.Мой метод проверки выглядит следующим образом:
static public void checkFunctionSpec(Callback cbk) {
Class[] prms=cbk.getParmTypes();
Class ret =cbk.getReturnType();
if(!Number.class.isAssignableFrom(ret)) {
throw new IllegalArgumentException(
"A function callback must return a Number type " +
"(any Number object or numeric primitive) - function '" +
cbk + "' is not permitted");
}
for(Class prm: prms) {
if(!Double.class.isAssignableFrom(prm)) {
throw new IllegalArgumentException(
"A function callback must take parameters of " +
"assignment compatible with double " +
"(a Double or Float object or a double or float primitive) " +
"- function '" + cbk + "' is not permitted");
}
}
}
Проблема, с которой я сталкиваюсь, заключается в том, что когда я пробую это, например.Math.abs() выдает исключение для возвращаемого типа следующим образом:
java.lang.IllegalArgumentException:
A function callback must return a Number type (any Number object or numeric primitive)
- function 'public static double java.lang.Math.abs(double)' is not permitted
Для меня это было удивительно, поскольку я ожидал, что примитивы будут просто работать, потому что (а) они отражаются с использованием своих классов-оболочек и (б) тип Double.TYPE объявлен как тип Class<Double>.
Кто-нибудь знает, как я могу добиться этого, не изменяя свои проверки:
if(!Number.class.isAssignableFrom(ret)
&& ret!=Double.TYPE
&& ret!=Float.TYPE
&& ret!=...) {
Разъяснение
Когда вы вызываете метод double abs(double)
используя Method.invoke(), вы передаете Object[]{Double} и возвращаете Double.Однако моя проверка, похоже, не удалась, поскольку Double.TYPE нельзя назначить Double.Поскольку мне требуется, чтобы все эти обратные вызовы возвращали какое-то число, которое будет возвращено вызовом() как число, я пытаюсь проверить, что предоставленный метод возвращает либо число, либо числовой примитив.
Проверка параметров аналогична.
Другими словами, при использовании отражения типы parm и return Double и double идентичны, и я хотел бы проверить их. легко как таковой.
РЕДАКТИРОВАТЬ:Чтобы уточнить:Я хочу проверить, что метод при вызове метода вызова() возвращает объект типа Number (из которого я могу вызвать obj.doubleValue(), чтобы получить желаемый двойной результат).
Решение
При более внимательном рассмотрении документации Class.isAssignableFrom() в ней конкретно указано, что типы примитива не соответствуют ни одному классу, кроме самого себя.Поэтому мне нужно будет специально проверить равенство == для Byte.TYPE, Double.TYPE, Float.TYPE, Integer.TYPE, Long.TYPE и Short.TYPE для типа возвращаемого значения.
Другие советы
Почему бы не поручить это компилятору?
public interface F<A, B> {
public B $(A a);
}
Тогда вы можете пройти F<Double, Double>
к методу, который ожидает F<? extends Number, ? extends Number>
.
РЕДАКТИРОВАТЬ:
Вы говорите, что хотите предоставить один класс для типа функции с любым количеством аргументов.Этот может быть выполнено с помощью системы типов Java.Концептуально каждая функция имеет только один аргумент.Функция с двумя аргументами эквивалентна функции, возвращающей другую функцию.Итак, вот переменная, значением которой является функция, принимающая два двойных значения:
F<Double, F<Double, Double>> f;
Вот метод, который передает два двойных значения данной функции:
public Double operate(F<Double, F<Double, Double>> f, double a, double b) {
return f.$(a).$(b);
}
Или рассмотрим тип L<A extends L>
с двумя подклассами C<E, T extends L<T>>
представляющий «минусы» и тип терминатора N
:
public abstract class L<A extends L<A>> {
private L() {}
private static final N nil = new N();
public static N nil() {
return nil;
}
public static final class N extends L<N> {
private N() {}
public <E> C<E, N> cons(final E e) {
return new C<E, L>(e, this);
}
}
public static final class C<E, L extends L<L>> extends L<C<E, L>> {
private E e;
private L l;
private C(final E e, final L l) {
this.e = e;
this.l = l;
}
public E head() {
return e;
}
public L tail() {
return l;
}
public <E> C<E, C<E, L>> cons(final E e) {
return new C<E, C<E, L>>(e, this);
}
}
}
В таком случае вы можете реализовать тип функции следующим образом:
public interface F<A extends L<A>, B> {
public B $(A args);
}
Следующий метод ожидает функцию с двумя Double
аргументы (и возвращает Double
), вместе с двумя double
s, чтобы применить его к:
public Double operate(F<C<Double, C<Double, N>>, Double> f, double a, double b) {
return f.$(N.nil().cons(b).cons(a));
}
Осуществление F
интерфейс должен будет получить аргументы из списка, используя head
и tail
.По сути, вы реализуете LISP на Java.:)
Сказав это, проверьте Функциональная Java, это библиотека, в которой уже есть много подобного.Я уверен, что есть еще один, который использует отражение, поэтому вам не придется писать его самостоятельно.
Параметр Math.abs() — это двойной примитив.Я не совсем понимаю, что вы подразумеваете под примитивом, «совместимым по присваиванию» с объектом (по сути, API отражения означает «может быть приведением»).Но если вы имеете в виду «может перейти в конструктор Double», то это, по сути, примитивный двойной элемент (или строка)!Возможно, вам нужно уточнить немного больше, что вам нужно сделать?