2012-12-22 13 views
33

ho metodoparametri Named

def test(String a, String b) { } 

e vorrei chiamare questo con una mappa parametro dinamico. Ho sempre però che

test(['1','2']); //valid call 

e anche

test([a:'1',b:'2']); //=> does not work 

lavorerò. ma non è così. Quindi mi sono ricordato di the spread operator, ma non riesco a farlo funzionare ...

C'è un modo per chiamare un metodo come quello sopra con un qualche tipo di mappa come parametro invece di singoli parametri?

+1

non ero a conoscenza Groovy supportato nominato parametri in questo momento ... Il tuo esempio non funziona nel mio groove-2.0.6 – Will

+0

Voi (in parte) a destra - Ho aggiornato la mia domanda. In parte perché il mio esempio è sbagliato, ma Groovy supporta i parametri denominati. http://mrhaki.blogspot.de/2009/09/groovy-goodness-named-parameters-are.html - hey, credo sia la soluzione alla mia domanda ... – rdmueller

risposta

23

Forse mi sono perso qualcosa, ma non credo che Groovy abbia chiamato parametri in questo momento. Ci sono discussions e proposals, ma non sono a conoscenza di nulla di ufficiale.

Per il tuo caso, penso che la mappa si diffonda può aiuto, ma non in ogni caso. Su ottenere i valori, ne segue l'ordine in cui sono stati dichiarati i valori della mappa:

def test(String a, String b) { "a=$a, b=$b" } 
def test(Map m) { test m*.value } 

assert test(a: "aa", b:"bb") == "a=aa, b=bb" 
assert test(b: "aa", a:"bb") != "a=aa, b=bb" // should be false :-(
assert test(b: "ccc", a:"ddd") == "a=ddd, b=ccc" // should have worked :-(

Per le classi, mi permetto di suggerire Groovy's as operator?

@groovy.transform.CompileStatic 
class Spread { 
    class Person { 
    String name 
    BigDecimal height 
    } 

    def method(Person p) { 
    "Name: ${p.name}, height: ${p.height}" 
    } 

    def method(Map m) { method m as Person } 

    static main(String[] args) { 
    assert new Spread().method(name: "John", height: 1.80) == 
     "Name: John, height: 1.80" 
    } 
} 
+0

great! Era esattamente quello che stavo cercando, ma mi mancava il valore in '* .valore'! Quindi questa è la soluzione alla mia domanda originale: 'test def (String a, String b) {} \ n test ([a: '1', b: '2'] *. Valore);' – rdmueller

+5

I'm Non sono sicuro, ma credo che questa risposta sia obsoleta. Vedi la risposta da @ Tom. – ben

26

Non dovrebbe essere la chiamata al metodo test(a:'1', b:'2'); invece di test([a:'1',b:'2']);?

Controllare gli argomenti denominati here.

+1

Sembra che entrambe le notazioni siano valide ... – rdmueller

+2

+1 per il collegamento – Will

+2

Il collegamento è morto :-(Ecco il [nuovo collegamento] (http://docs.groovy-lang.org/latest/html/documentation/ #_named_arguments) :-) – Craig

5

Grazie al commento di Will P, ho trovato una soluzione che si adatta il mio problema:

se mi definisco un parametro senza un tipo, posso passare in tutti i tipi di tipi, tra cui HashMaps. E Groovy trasforma un costrutto come a:'h',b:'i' automagicamente in un HashMap

def test(myParams, Integer i) { 
    return myParams.a + myParams.b 
} 

assert test(a:'h',b:'i',5) == test(b:'i',a:'h',5) 
assert test([a:'h',b:'i'],5) == test(b:'i',a:'h',5) 
test('h','i',5); //still throws an exception 

In questo modo, posso usare singoli parametri denominati, ma può utilizzare una mappa troppo!

+1

Ho pensato che stavi cercando qualcosa come i parametri con nome python (http://davedash.com/2007/12/21/python-named-arguments-pure-genius/). Sono contento che tu abbia trovato la soluzione, comunque :-) – Will

4

Il supporto dei parametri con nome è abbastanza flessibile, ma i documenti sono un po 'sottili. Ecco alcune delle regole che ho scoperto. Nota che sto cercando di essere univoca nel manuale di parametri (dichiarati nel metodo) e args (passato alla chiamata di metodo)

  • Il parametro Map prima deve essere dichiarato prima. Questo è il grande. E non ovvio.
  • non hai bisogno di una completa mappa nel args, solo gli elementi della mappa cioè (a: "aa") è abbastanza buono, non è necessario ([a: "aa"])
  • Potete mescolarti avete ordinato args (senza nome) con il nome args, purché come il gli argomenti ordinati rimangono nello stesso ordine dei parametri che riempiono
  • È possibile interspondere gli argomenti denominati con gli argomenti ordinati regolari. Questo è abbastanza bello, ma di nuovo gli argomenti ordinati devono essere in, bene, ordine.
  • È anche possibile utilizzare i parametri ordinati opzionali con lo stesso metodo firma (vedi x negli esempi qui sotto)
  • si può dare il parametro Map un default vuota mappa args=[:] rendendo il nome args facoltativa, ma questo non lo fa funzionare bene se si dispone di altri parametri opzionali (vedi ultimi esempi di seguito)

ecco alcuni esempi: Le params non hanno bisogno di essere digitato, ma ho tipi aggiunto per chiarezza.

// this method has a map args to capture all named args 
// and non-named (ordered) args String s, int n, and int x 
// x has a default value so is optional 
// the map (here untyped) to capture the nameed args MUST COME FIRST 
def m(Map args=[:], String s, int n, int x=1) 
{ 
    println "s:$s n:$n x:$x, args:$args" 
} 

//1: pass in named args first, then ordered 
m(a: "aa", b: 3, "ss", 44, 5) // s:s n:44 x:5, args:[a:aa, b:3] 

//2: ordered args first - named args last (same result) 
m("ss", 44, 5, a: "aa", b: 3) // s:s n:44 x:5, args:[a:aa, b:3] 

//3: bring the first ordered arg (s) to the start (same result) 
m("ss", a: "aa", b: 3, 44, 5) // s:s n:44 x:5, args:[a:aa, b:3] 

//4: stick the ordered arg n in the middle of the named args (same result!) 
m("ss", a: "aa", 44, b: 3, 5) // s:s n:44 x:5, args:[a:aa, b:3] 


//5: mix the ordered args in with the named and SKIP the arg x with default value (x=1) 
m(a: "aa", "ss", b: 3, 44) // s:ss n:44 x:1, args:[a:aa, b:3] 

//6: ordered arg n first - so in the wrong order (Fail!) 
//m(44, "ss", a: "aa", b: 3, 5) //MissingMethodException: No signature .. of .. m() .. applicable for 
          // argument types: (java.util.LinkedHashMap, java.lang.Integer, java.lang.String, java.lang.Integer) 
          // values: [[a:aa, b:3], 44, ss, 5] 

//7: no named args: Fails! (change signature to add default: Map args=[:] and it will succeed with: s:ss n:44 x:1, args:[:] 
m("ss", 44) // ...No signature ... applicaple ... types (java.lang.String, java.lang.Integer) 

//8: no named args: Fails! (even with default map in signature this fails!) 
m("ss", 44, 5) // ...No signature ... applicaple ... types (java.lang.String, java.lang.Integer, java.lang.Integer)