Pregunta

Tengo un objeto Callback genérico que proporciona una capacidad de devolución de llamada (primitiva) para Java, en ausencia de cierres.El objeto Callback contiene un método y devuelve el parámetro y los tipos de retorno del método a través de un par de métodos de acceso que simplemente delegan a los métodos equivalentes en Método.

Estoy intentando validar que una devolución de llamada que me han proporcionado apunte a un método válido.Necesito que la asignación del tipo de retorno sea compatible con Número y que todos los parámetros sean compatibles con la asignación Doble.Mi método de validación se ve así:

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");
            }
        }
    }

El problema que encuentro es que cuando intento esto, p.e.Math.abs(), genera una excepción para el tipo de retorno de la siguiente manera:

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

Esto me sorprendió porque esperaba que las primitivas simplemente funcionaran porque (a) se reflejan usando sus clases contenedoras y (b) Double.TYPE se declara como de tipo Class<Double>.

¿Alguien sabe cómo puedo lograr esto sin modificar mis controles para que sean:

if(!Number.class.isAssignableFrom(ret)
     && ret!=Double.TYPE
     && ret!=Float.TYPE
     && ret!=...) {

Aclaración

Cuando invocas el método double abs(double) usando Method.invoke(), pasas un Object[]{Double} y obtienes un Double.Sin embargo, mi validación parece estar fallando porque Double.TYPE no se puede asignar a Double.Dado que necesito que todas estas devoluciones de llamada devuelvan algún tipo de número, que será devuelto por invoke() como un Número, estoy intentando validar que el método proporcionado devuelva un Número o una primitiva numérica.

La validación de los parámetros es igualmente.

En otras palabras, cuando se utiliza la reflexión, los tipos parm y return Double y double son idénticos y me gustaría validarlos. fácilmente como tal.

EDITAR:Para aclarar más:Quiero validar que un método, cuando se llama a invoke(), devolverá un objeto de tipo Número (desde el cual puedo llamar a obj.doubleValue() para obtener el doble que quiero).

¿Fue útil?

Solución

Mirando más de cerca la documentación de Class.isAssignableFrom (), establece específicamente que los tipos para una primitiva no coinciden con ninguna clase excepto con ellos mismos. Por lo tanto, tendré que verificar específicamente == igualdad en Byte.TYPE, Double.TYPE, Float.TYPE, Integer.TYPE, Long.TYPE y Short.TYPE para el tipo de retorno.

Otros consejos

¿Por qué no dejar que el compilador lo haga?

public interface F<A, B> {
   public B $(A a);
}

Entonces puedes pasar un F<Double, Double> a un método que espera un F<? extends Number, ? extends Number>.

EDITAR:

Dice que desea proporcionar una única clase para el tipo de función con cualquier número de argumentos.Este poder hacerse con el sistema de tipos Java.Conceptualmente cada función tiene un solo argumento.Una función con dos argumentos equivale a una función que devuelve otra función.Entonces aquí hay una variable cuyo valor es una función que toma dos dobles:

F<Double, F<Double, Double>> f;

Aquí hay un método que pasa dos dobles a una función determinada:

public Double operate(F<Double, F<Double, Double>> f, double a, double b) {
   return f.$(a).$(b);
}

O considere un tipo L<A extends L> con dos subclases C<E, T extends L<T>> representando un "contras" y un tipo de terminador 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);
   }  
 }  

}  

En tal caso, puedes implementar un tipo de función de la siguiente manera:

public interface F<A extends L<A>, B> {
   public B $(A args);
}

El siguiente método espera una función con dos Double argumentos (y devuelve un Double), junto con dos doubles para aplicarlo a:

public Double operate(F<C<Double, C<Double, N>>, Double> f, double a, double b) {
   return f.$(N.nil().cons(b).cons(a));
}

La implementación de la F La interfaz tendría que obtener los argumentos de la lista usando. head y tail.Entonces, en efecto, estás implementando LISP en Java.:)

Dicho esto, echa un vistazo Java funcional, que es una biblioteca que ya tiene muchas de estas cosas.Estoy seguro de que también existe uno que utiliza la reflexión para que no tengas que escribirlo tú mismo.

El parámetro para Math.abs () es la doble primitiva. No estoy muy seguro de lo que quieres decir con un ser primitivo & Quot; asignación compatible & Quot; con un objeto (lo que la API de reflexión significa esencialmente es " puede ser un reparto de "). Pero si te refieres a & "; Puede pasar a un constructor doble &"; ¡Entonces eso es esencialmente un doble primitivo (o una cadena)! ¿Quizás necesita aclarar un poco más lo que debe hacer?

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top