Reputation: 64036
I have a generic Callback object which provides a (primitive) callback capability for Java, in the absence of closures. The Callback object contains a Method, and returns the parameter and return types for the method via a couple of accessor methods that just delegate to the equivalent methods in Method.
I am trying to validate that a Callback I have been supplied points to a valid method. I need the return type assignment compatible with Number and all parameters to be assignment compatible with Double. My validating method looks like this:
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");
}
}
}
The problem I encounter is that the when I try this with, e.g. Math.abs(), it's throwing an exception for the return type as follows:
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
This was surprising to me because I expected primitives to simply work because (a) they are reflected using their wrapper classes, and (b) the Double.TYPE is declared to be of type Class<Double>.
Does anyone know how I can achieve this without modifying my checks to be:
if(!Number.class.isAssignableFrom(ret)
&& ret!=Double.TYPE
&& ret!=Float.TYPE
&& ret!=...) {
When you invoke the method double abs(double)
using Method.invoke(), you pass in a Object[]{Double} and get back a Double. However, my validation appears to be failing because Double.TYPE is not assignable to a Double. Since I require all these callbacks to return some sort of number, which will be returned by invoke() as a Number, I am trying to validate that the supplied method returns either Number or a numeric primitive.
Validation of the parms is likewise.
In other words, when using reflection the parm and return types Double and double are identical and I would like to validate them easily as such.
EDIT: To further clarify: I want to validate that a Method will, when invoke() is called return an Object of type Number (from which I can call obj.doubleValue() to get the double I want).
Upvotes: 0
Views: 2968
Reputation: 35054
Why not have the compiler do it?
public interface F<A, B> {
public B $(A a);
}
Then you can pass an F<Double, Double>
to a method that expects an F<? extends Number, ? extends Number>
.
EDIT:
You say you want to provide a single class for the type of a function with any number of arguments. This can be done with the Java type system. Conceptually every function has only one argument. A function with two arguments is equivalent to a function that returns another function. So here's a variable whose value is a function that takes two doubles:
F<Double, F<Double, Double>> f;
Here's a method that passes two doubles to a given function:
public Double operate(F<Double, F<Double, Double>> f, double a, double b) {
return f.$(a).$(b);
}
Or, consider a type L<A extends L>
with two subclasses C<E, T extends L<T>>
representing a "cons", and a terminator type 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);
}
}
}
In such a case, you can implement a function type thusly:
public interface F<A extends L<A>, B> {
public B $(A args);
}
The following method expects a function with two Double
arguments (and returns a Double
), along with two double
s to apply it to:
public Double operate(F<C<Double, C<Double, N>>, Double> f, double a, double b) {
return f.$(N.nil().cons(b).cons(a));
}
The implementation of the F
interface would have to get the arguments from the list using head
and tail
. So in effect, you're implementing LISP in Java. :)
Having said that, check out Functional Java, which is a library that has a lot of this stuff already. I'm sure there's also one out there that uses reflection so you don't have to write it yourself.
Upvotes: 1
Reputation: 64036
Looking more closely at the documentation for Class.isAssignableFrom(), it specifically states that the types for a primitive do not match any class except themselves. So I will need to specifically check for == equality to Byte.TYPE, Double.TYPE, Float.TYPE, Integer.TYPE, Long.TYPE, and Short.TYPE for the return type.
Upvotes: 1
Reputation: 21795
The parameter to Math.abs() is the double primitive. I'm not quite sure what you mean by a primitive being "assignment compatible" with an object (what the reflection API essentially means is "can be a cast of"). But if you mean "can pass into a Double constructor", then that's essentially a primitive double (or a string)!! Perhaps you need to clarify a bit more what you need to do?
Upvotes: 0