2016-07-07 72 views
6

Sto faticando un po 'a capire le funzioni di ordine superiore e come passare le funzioni come parametri ad altre funzioni usando Kotlin. Ho un esempio di base che voglio fufill:Come funzionano le funzioni di ordine superiore di Kotlin?

fun addOnSearchGameResultListener(
      activity: AppCompatActivity, 
      releaseThread:() -> Unit, 
      showNoResultsFoundMessage:() -> Unit, 
      updateSearchResults: (result: List<Game>) -> Unit) { 
     var event0017Handler: TaskExecutor = object : TaskExecutor { 
      override fun executeOnSuccessTask(response: JSONObject) { 
       async() { 
        uiThread { 
         try { 
          releaseThread() 
          mLoaderManager.hideIndeterminateProgressBar(activity) 
          val result = mJSONParser.getGamesByGameKey(response) 
          Log.i(GameController::class.simpleName, "response: ${result.toString()}") 
          updateSearchResults(result) 
         } catch (e: JSONException) { 
          showNoResultsFoundMessage() 
         } 
        } 
       } 
      } 

      override fun executeOnErrorTask(payload: JSONObject) { 
       releaseThread() 
       mNotificationManager.showErrorPopUp(activity, payload.getString("data")) 
      } 
     } 
     NotificationCenter.RegistrationCenter.registerForEvent(EventCatalog.e0017, event0017Handler) 
    } 

sto chiamando il metodo di cui sopra in questo modo:

mGameService.addOnSearchGameResultListener(
      this, 
      releaseThread(), 
      showNoResultsFoundMessage(), 
      updateSearchResults(null) 
    ) 

E updateSearchResults(null) è dichiarato come:

private fun updateSearchResults (results : List<Game>?) : (results : List<Game>?) -> Unit = { 
     if (null != results && results.size > 0) { 
      mLastMatchingQuery = query_container.text.toString() 
      hideNoResultsFoundMessage() 
      mGames = results 
      mAdapter!!.dataSet = results.toMutableList() 
     } else { 
      showNoResultsFoundMessage() 
     } 
    } 

lo so Ho passato null alla funzione quando l'ho dichiarata (perché ho bisogno di passare qualcosa al momento della compilazione), tuttavia, la chiamata effettuata all'interno di addOnSearchGameResultListener() non viene eseguita passando il parametro dal runtime, Voglio dire, in addOnSearchGameResultListener() ottengo sempre null per i risultati. Come funziona esattamente e cosa sto sbagliando?

risposta

2

Penso che la confusione derivi dai nomi dei parametri, results in particolare. Per risolvere che è possibile cambiare il updateSearchResults a IE:

private fun updateSearchResults() : (List<Game>?) -> Unit = { results -> 
    if (null != results && results.size > 0) { 
     mLastMatchingQuery = query_container.text.toString() 
     hideNoResultsFoundMessage() 
     mGames = results 
     mAdapter!!.dataSet = results.toMutableList() 
    } else { 
     showNoResultsFoundMessage() 
    } 
} 

Tuttavia mi sento che sarebbe più facile da seguire il codice se si desidera applicare le seguenti modifiche:

  • fanno updateSearchResults metodo regolare:

    private fun updateSearchResults (results : List<Game>?) { 
        if (null != results && results.size > 0) { 
         mLastMatchingQuery = query_container.text.toString() 
         hideNoResultsFoundMessage() 
         mGames = results 
         mAdapter!!.dataSet = results.toMutableList() 
        } else { 
         showNoResultsFoundMessage() 
        } 
    } 
    
  • modifica la addOnSearchGameResultListener invocazione e passare un lambda

    mGameService.addOnSearchGameResultListener(
         this, 
         releaseThread(), 
         showNoResultsFoundMessage(), 
         { updateSearchResults(it) } 
    ) 
    
  • applicare le modifiche simili a releaseThread, showNoResultsFoundMessage

+0

Grazie mille, come ha sottolineato @voddan e hai risolto nella risposta sopra, non stavo usando il parametro. Alla fine ho seguito il tuo annuncio e reso le funzioni regolari e specificato il lambda nella chiamata. Puoi chiarire per favore cosa esattamente ha passato "it" come parametro ?: –

+1

@EdgarDaSilvaFernandes [Un'altra utile convenzione è che se una funzione letterale ha un solo parametro, la sua dichiarazione può essere omessa (insieme a ->), e il suo nome sarà 'it'] (https://kotlinlang.org/docs/reference/lambdas.html) – miensol

2

francamente, non sono del tutto sicuro di quello che il codice era quello di raggiungere, ma vorrei chiarire che cosa il vostro frammento sta facendo almeno:

private fun updateSearchResults(results : List<Game>?): 
     (foo: List<Game>?) -> Unit = { parameter: List<Game>? -> 

    if (null != results && results.size > 0) { 
     // code 
     Unit 
    } else { 
     // code 
     Unit 
    } 
} 

Qui si ha una funzione updateSearchResults che accetta un parametro results e restituisce una funzione di tipo (foo: List<Game>?) -> Unit. Nota che ho rinominato alcune cose per evitare scontri sul nome e chiarire cosa è cosa. La denominazione foo non ha alcun effetto in tal modo, non sono sicuro del motivo per cui è consentito scriverlo. Il ritorno lambda ha un parametro parameter di tipo List<Game>?, che ignori completamente nel tuo codice. Complessivamente, il risultato di if dipende esclusivamente dal parametro updateSearchResults.

+0

ho capito il mio errore ora, è quello che hai detto, ho completamente ignorato il parametro della funzione L'idea alla base del codice è che la funzione di ordine superiore si trova in una classe diversa rispetto ai risultati dell'aggiornamento e voglio mantenerla in questo modo. Grazie per il chiarimento! –

+1

Benvenuto! Sembra che tu stia facendo cose abbastanza avanzate senza leggere i documenti. Vi raccomando di leggere la documentazione sul sito almeno dall'inizio alla metà. È piuttosto breve – voddan

+1

Hai ragione, sono stato troppo entusiasta della lingua e volevo iniziare a utilizzare le sue funzionalità appena possibile, ma lo leggerò di nuovo come hai suggerito. Grazie ancora! –

0

ho passato nulla al func quando ho dichiarato che (perche 'ho bisogno di passare qualcosa al momento della compilazione), tuttavia, la chiamata fatta dall'interno addOnSearchGameResultListener() non viene effettuata passando il parametro dal runtime

non si passa in fase di esecuzione o tempo di compilazione.Se si utilizza la funzione solo una volta come updateSearchResults(null), il if è sempre falso, e il tutto è equivalente a { showNoResultsFoundMessage() }

0

C'è un excellent article creato da Juan Ignacio Saravia che parlano di funzioni di ordine superiore

Proverò a sintetizzare qui:

Una funzione di ordine superiore è una funzione che assume le funzioni di parametri o restituisce una funzione.

passa una funzione come parametro di

fun logExecution(func:() -> Unit) { 
    Log.d("tag", "before executing func") 
    func() 
    Log.d("tag", "after executing func") 
} 

Questa funzione “logExecution” consente di passare una funzione come parametro e si accede prima e dopo l'esecuzione di questa funzione.

func() -> Unità

Qui “func” è il nome del parametro e “() -> Unità” è il “tipo” del parametro, in questo caso, stiamo dicendo che func sarà una funzione che non riceve alcun parametro e non restituisce alcun valore (ricorda che l'unità funziona come nulla in Java).

È possibile chiamare questa funzione passando un'espressione lambda che non deve ricevere o restituire qualsiasi valore, come in questo modo:

logExecution({ Log.d("tag", "I'm a function") }) 

ma anche Kotlin permette di rimuovere la parentesi se v'è una sola funzione parametro o se l'ultimo parametro è una funzione:

logExecution { Log.d("tag", "I'm a function") } 

ricevere un altro parametro

Possiamo cambiare la firma logExecution per ricevere un altro parametro e poi mettere il parametro della funzione alla fine in questo modo:

// added tag parameter: 
fun logExecution(tag: String, func:() -> Unit) { ... } 
// call in this way: 
logExecution("tag") { Log.d("tag", "I'm a function") } 

o:

logExecution("tag") { 
    Log.d("tag", "I'm a function") 
} 

rendere la funzione di ricezione e valori di ritorno

fun logExecution(func: (String, String) -> Int) { 
    val thisIsAnInt = func("Hello", "World") 
} 

Esempio di funzione asincrona

Questa è una funzione che riceve una funzione ed eseguirlo in un altro thread:

fun runAsync(func:() -> Unit) { 
    Thread(Runnable { func() }).start() 
} 

e siamo in grado di eseguire una funzione di fuori del thread principale interfaccia utente facile:

runAsync { 
    // i.e.: save something in the Database 
} 

Forse si vuole per eseguire un codice specifico per i dispositivi Lollipop e invece di eseguire il controllo regolare se, è possibile utilizzare questa funzione:

fun isLollipopOrAbove(func:() -> Unit) { 
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 
     func() 
    } 
} 

e usarlo in questo modo:

isLollipopOrAbove { 
    // run lollipop specific code safely 
} 

Spero con questo, è diventato un po 'più chiaro su funzioni di ordine superiore