我有一个通用的Callback对象,它在没有闭包的情况下为Java提供(原始)回调功能。 Callback对象包含一个Method,并通过一些只委托给Method中等效方法的访问器方法返回方法的参数和返回类型。
我正在尝试验证我提供的回调指向有效方法。我需要与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
这对我来说是令人惊讶的,因为我希望原语能够简单地工作,因为(a)它们使用它们的包装类来反映,并且(b)Double.TYPE被声明为Class< Double>类型。
有人知道如何在不修改我的支票的情况下实现这一目标:
if(!Number.class.isAssignableFrom(ret)
&& ret!=Double.TYPE
&& ret!=Float.TYPE
&& ret!=...) {
使用Method.invoke()调用方法double abs(double)
时,传入Object [] {Double}并返回Double。但是,我的验证似乎失败,因为Double.TYPE不能分配给Double。因为我要求所有这些回调返回某种数字,这将由invoke()作为数字返回,我试图验证提供的方法返回Number或数字原语。
同样验证了parms。
换句话说,当使用反射时,parm和返回类型Double和double是相同的,我想验证它们很容易。
编辑:进一步澄清:我想验证一个方法将在调用invoke()时返回一个Number类型的Object(我可以从中调用obj.doubleValue()来获取我想要的double)。 / p>
答案 0 :(得分:1)
为什么不让编译器这样做?
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>>
代表“cons”,终结器类型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
来将其应用于:
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
从列表中获取参数。因此,实际上,您正在使用Java实现LISP。 :)
话虽如此,请查看Functional Java,这是一个已经拥有大量此类内容的库。我相信还有一个使用反射,所以你不必自己写。
答案 1 :(得分:1)
仔细查看Class.isAssignableFrom()的文档,它特别指出基元的类型与除自身之外的任何类都不匹配。所以我需要专门检查==等于Byte.TYPE,Double.TYPE,Float.TYPE,Integer.TYPE,Long.TYPE和Short.TYPE作为返回类型。
答案 2 :(得分:0)
Math.abs()的参数是双原语。我不太清楚你对一个与对象“赋值兼容”的原语是什么意思(反射API本质上意味着“可以是一个演员”)。但如果你的意思是“可以传递给Double构造函数”,那么这本质上是一个原始的双(或字符串)!!也许你需要澄清一下你需要做些什么?