Domanda

Ho un oggetto Callback generico che fornisce una funzionalità (primitiva) di callback per Java, in assenza di chiusure. L'oggetto Callback contiene un metodo e restituisce il parametro e restituisce i tipi per il metodo tramite un paio di metodi di accesso che delegano ai metodi equivalenti in Metodo.

Sto provando a convalidare che a un Callback che mi è stato fornito punta un metodo valido. Ho bisogno che l'assegnazione del tipo di ritorno sia compatibile con Number e che tutti i parametri siano compatibili con Double. Il mio metodo di convalida è simile al seguente:

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

Il problema che incontro è che quando provo questo, ad es. Math.abs (), sta generando un'eccezione per il tipo restituito come segue:

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

Questo è stato sorprendente per me perché mi aspettavo che le primitive funzionassero semplicemente perché (a) vengono riflesse usando le loro classi wrapper e (b) Double.TYPE è dichiarato di tipo Class < Double gt;.

Qualcuno sa come posso raggiungere questo obiettivo senza modificare i miei controlli in:

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

Chiarimento

Quando invochi il metodo double abs(double) usando Method.invoke (), passi un oggetto [] {Double} e ricevi un doppio. Tuttavia, la mia convalida sembra non riuscire perché Double.TYPE non è assegnabile a un Double. Poiché ho bisogno che tutti questi callback restituiscano una sorta di numero, che verrà restituito da invoke () come numero, sto cercando di convalidare che il metodo fornito restituisce il numero o una primitiva numerica.

La convalida dei parametri è parimenti.

In altre parole, quando si usa la riflessione i tipi parm e return sono identici e doppi e mi piacerebbe validarli facilmente come tali.

MODIFICA: Per chiarire ulteriormente: voglio confermare che un metodo, quando invoke () viene chiamato restituisce un oggetto di tipo Numero (da cui posso chiamare obj.doubleValue () per ottenere il doppio che desidero).

È stato utile?

Soluzione

Osservando più da vicino la documentazione per Class.isAssignableFrom (), si afferma specificamente che i tipi per una primitiva non corrispondono a nessuna classe tranne a loro stessi. Quindi dovrò verificare in modo specifico == uguaglianza con Byte.TYPE, Double.TYPE, Float.TYPE, Integer.TYPE, Long.TYPE e Short.TYPE per il tipo restituito.

Altri suggerimenti

Perché non farlo fare al compilatore?

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

Quindi puoi passare un F<Double, Double> a un metodo che prevede un F<? extends Number, ? extends Number>.

Modifica

Dici di voler fornire una singola classe per il tipo di una funzione con un numero qualsiasi di argomenti. Questo può essere eseguito con il sistema di tipi Java. Concettualmente ogni funzione ha solo un argomento. Una funzione con due argomenti equivale a una funzione che restituisce un'altra funzione. Quindi ecco una variabile il cui valore è una funzione che accetta due doppie:

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

Ecco un metodo che passa due doppie a una determinata funzione:

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

Oppure, considera un tipo L<A extends L> con due sottoclassi C<E, T extends L<T>> che rappresentano un " contro " e un tipo di terminazione 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 tal caso, è possibile implementare un tipo di funzione in questo modo:

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

Il seguente metodo prevede una funzione con due Double argomenti (e restituisce un double), insieme a due F a cui applicarlo:

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

L'implementazione dell'interfaccia head dovrebbe ottenere gli argomenti dall'elenco usando tail e <=>. Quindi, in effetti, stai implementando LISP in Java. :)

Detto questo, dai un'occhiata a Functional Java , che è una libreria che contiene già molte cose. Sono sicuro che ce n'è anche uno là fuori che usa la riflessione, quindi non devi scriverlo da solo.

Il parametro di Math.abs () è la doppia primitiva. Non sono del tutto sicuro di cosa intendi per essere primitivo & Quot; assegnazione compatibile & Quot; con un oggetto (ciò che l'API di riflessione essenzialmente significa " può essere un cast di "). Ma se intendi & Quot; può passare in un doppio costruttore & Quot ;, allora questo è essenzialmente un doppio primitivo (o una stringa) !! Forse devi chiarire un po 'di più cosa devi fare?

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top