2015-10-21 10 views
8

Supponiamo che abbia una stringa: String s = "1,2,3,4,5,6". Vorrei creare un metodo combineFunctions() che prendesse una sequenza di lunghezza variabile di Function s come argomento e applicasse tutte le operazioni in quell'ordine.Creare un metodo che accetta la lunghezza variabile di argomenti Function con tipi possibilmente diversi

Le funzioni possono avere diversi tipi <T,U>.

Esempio utilizza di tale funzione potrebbe essere il seguente:

Combine<String> c = new Combine<>(s); 
List<String> numbers = c.combineFunctions(splitByComma); 
Integer max = c.combineFunctions(splitByComma,convertToInt, findMax); 

Quello che ho cercato (la <U> qui non è l'uso di molto qui):

public <U> void combineFunctions(
     Function<? extends Object, ? extends Object>... functions) { 

} 

Ma io sono bloccato a ricevendo il tipo dell'ultimo dei Function s. Stavo anche pensando a un approccio ricorsivo, ma il parametro varargs deve essere l'ultimo.

Sarebbe possibile implementare tale metodo in Java?

+0

cosa intendi con questo "Ma sono bloccato a ottenere il tipo dell'ultima delle funzioni". puoi spiegare per favore? –

+4

Sembra 'Stream' e il metodo' map'. – gontard

+3

Se si sa che verrà sempre chiamato con un numero relativamente piccolo di argomenti (ad esempio meno di 10 funzioni) è possibile scrivere 10 metodi 'combinati' (Funzione, Funzione ) '. Questo è un modo standard per mantenere la sicurezza del tipo accettando un numero (ma limitato) di argomenti. – assylias

risposta

3

Il problema con tale funzione è che si perde il controllo del tipo in fase di compilazione e la trasmissione è necessaria.

Questa sarebbe un'implementazione, utilizzando andThen per combinare le funzioni insieme. Questo sembra brutto a causa di tutto il casting e non sono sicuro che tu possa farlo più correttamente. Si noti inoltre che ciò richiede la creazione di 2 condotte Stream se solo 1 è realmente necessario.

public static void main(String[] args) throws Exception { 
    String str = "1,2,3,4,5,6"; 
    Function<Object, Object> splitByComma = s -> ((String) s).split(","); 
    Function<Object, Object> convertToInt = tokens -> Stream.of((String[]) tokens).map(Integer::valueOf).toArray(Integer[]::new); 
    Function<Object, Object> findMax = ints -> Stream.of((Integer[]) ints).max(Integer::compare).get(); 
    Integer max = (Integer) combineFunctions(splitByComma, convertToInt, findMax).apply(str); 
    System.out.println(max); 
} 

@SafeVarargs 
private static Function<Object, Object> combineFunctions(Function<Object, Object>... functions) { 
    return Arrays.stream(functions) 
       .reduce(Function::andThen) 
       .orElseThrow(() -> new IllegalArgumentException("No functions to combine")); 
} 

per abbinare il codice nella tua domanda, si potrebbe avvolgere questo in una classe come questa:

public class Combiner<R> { 

    private Object input; 

    public Combiner(Object input) { 
     this.input = input; 
    } 

    @SuppressWarnings("unchecked") 
    @SafeVarargs 
    public final R combineFunctions(Function<Object, Object>... functions) { 
     return (R) Arrays.stream(functions) 
         .reduce(Function::andThen) 
         .orElseThrow(() -> new IllegalArgumentException("No functions to combine")) 
         .apply(input); 
    } 

} 
+0

Mi dispiace ma è stato un mio errore: il metodo non deve essere statico e penso che sia effettivamente impossibile implementare un metodo * statico * in un modo che ho dimostrato nella mia domanda (modificata la domanda). Sembra che i tuoi suggerimenti mi portino alla soluzione corretta. – syntagma

+1

@REACHUS Questo è solo un codice di esempio. Il codice del metodo può essere inserito all'interno di una classe e chiamato in contesto non statico, ovviamente, vedere la mia modifica. – Tunaki

+0

Grazie, il codice che hai aggiunto è quello che ho appena scoperto.Ho capito correttamente che in questo caso sarà creato solo 1 stream? – syntagma

3

L'esempio in questione è abbastanza facilmente risolto utilizzando lo stile funzionale Streams. L'approccio "funzionale" alla risoluzione di questo è usando le sequenze delle operazioni map, ogni fase che trasforma gli elementi in un tipo diverso e quindi opzionalmente riducendo/raccogliendo il risultato.

Per esempio

String str = "1,2,3,4,5,6,7"; 
int max = Arrays.stream(str.split(",")).mapToInt(Integer::parseInt).max().orElse(0) 

Lo stesso schema si applica per altri tipi di "combinazioni funzione".