L. Cornelius Dol
L. Cornelius Dol

Reputation: 64036

Validate reflected method return type and parms in Java

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!=...) {

Clarification

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

Answers (3)

Apocalisp
Apocalisp

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 doubles 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

L. Cornelius Dol
L. Cornelius Dol

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

Neil Coffey
Neil Coffey

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

Related Questions