2015-08-10 11 views
16

Sto sviluppando un'applicazione JavaFX8 in Scala ma non sono riuscito a capire come passare un riferimento al metodo su un gestore di eventi. Per chiarire, non sto usando la libreria ScalaFX ma costruisco la mia applicazione direttamente su JavaFX.È possibile utilizzare riferimenti al metodo di stile Java 8 in Scala?

Ecco lo snippet di codice correlato.

InputController.java (ho scritto questa classe di test in Java per isolare il problema di consumare un solo metodo di riferimento)

public class InputController { 
    public void handleFileSelection(ActionEvent actionEvent){ 
     //event handling code 
    } 

    public InputController() { 
     //init controller 
    } 
} 

Questo funziona (Java)

InputController inputController = new InputController(); 
fileButton.setOnAction(inputController::handleFileSelection); 

Questo non funziona (Scala)

val inputController = new InputController 
fileButton.setOnAction(inputController::handleFileSelection) 

Ecco il messaggio di errore del compilatore (Scala 2.11.6).

Error:(125, 45) missing arguments for method handleFileSelection in class Main; 
follow this method with '_' if you want to treat it as a partially applied function 
    fileButton.setOnAction(inputController::handleFileSelection) 
              ^

Se utilizzo Scala 2.12.0-M2, viene visualizzato un messaggio di errore diverso.

Error:(125, 45) missing argument list for method handleFileSelection in class Main 
Unapplied methods are only converted to functions when a function type is expected. 
You can make this conversion explicit by writing `handleFileSelection _` or `handleFileSelection(_)` instead of `handleFileSelection`. 
    fileButton.setOnAction(inputController::handleFileSelection) 
              ^

C'è un modo nativo con cui Scala può sfruttare i riferimenti ai metodi introdotti in Java 8? Sono a conoscenza dell'approccio implicito delle conversioni all'uso di un'espressione lambda, ma voglio sapere se esiste un modo per utilizzare un riferimento al metodo simile a Java 8 senza la necessità di utilizzare la decodifica lambda.

+0

Nota: Java non ha riferimenti al metodo. Puoi avere un oggetto che ha un metodo che chiama un altro metodo (che è quello che fa realmente lambda) –

+0

Penso di essere confuso con il tuo commento "Java non ha riferimenti al metodo". Ho pensato che quello che sto cercando di fare non è diverso dall'esempio ComparisonProvider nella documentazione ufficiale (link: https://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html). – Mustafa

+0

Questa domanda è fondamentalmente un duplicato di http://stackoverflow.com/questions/24369449/ in breve, è possibile creare una conversione implicita da (T) => Unit a EventHandler [T] e importarla implicitamente. –

risposta

12

inputController::handleFileSelection è la sintassi di Java, che non è supportato o necessaria a Scala, perché già ha avuto un breve sintassi per lambda come questo: inputController.handleFileSelection _ oppure inputController.handleFileSelection(_) (inputController.handleFileSelection può anche funzionare, a seconda del contesto).

Tuttavia, in Java è possibile utilizzare lambdas e riferimenti ai metodi quando è prevista un'interfaccia SAM (singolo metodo astratto) e EventHandler è proprio tale interfaccia. In Scala prima della versione 2.11 questo non è assolutamente consentito, in 2.11 esiste un supporto sperimentale per l'utilizzo di lambdas con le interfacce SAM, che deve essere abilitato usando il flag scalac -Xexperimental, e a partire da 2.12 è completamente supportato e non ha bisogno di essere abilitato.

+0

Quando abilito le funzionalità sperimentali, 'fileButton.setOnAction (inputController.handleFileSelection)' compila senza errori. Tuttavia, invece di essere passato come riferimento, il metodo 'handleFileSelection' viene eseguito immediatamente quando viene eseguita questa riga. Immagino sia per questo che è ancora una funzionalità sperimentale. – Mustafa

+0

ciò che ha funzionato per me è abilitare le funzionalità sperimentali e lasciare che l'espressione lambda gestisca il riferimento al metodo come segue. 'fileButton.setOnAction ((event: ActionEvent) => inputController.handleFileSelection)'. – Mustafa

+0

Vuoi dire '(event: ActionEvent) => inputController.handleFileSelection (event)'? La versione del tuo commento in realtà non dovrebbe funzionare. –

3

si dovrebbe passare funzione che l'applicazione di un parametro di tipo ActionEvent:

val button = new Button() 
val inputController = new InputController() 

def handler(h: (ActionEvent => Unit)): EventHandler[ActionEvent] = 
    new EventHandler[ActionEvent] { 
    override def handle(event: ActionEvent): Unit = h(event) 
    } 

button.setOnAction(handler(inputController.handleFileSelection)) 
+0

Sfortunatamente, questo non funziona. 'trovati: Unità richiesto: javafx.event.EventHandler [javafx.event.ActionEvent]' posso farlo funzionare cambiando la firma del metodo a 'EventHandler pubblico handleFileSelection() {' ma che sfida l'intero scopo di usando un riferimento al metodo. – Mustafa

+0

@Mustafa perché non hai provato a passare 'inputController'? Sembra un'implementazione dell'interfaccia di destinazione, no? –

+0

@Mustafa se vuoi usare il metodo di riferimento dovremmo parlare di alcune funzioni (consumatore, fornitore ecc.) Cosa si aspetta 'fileButton.setOnAction'? –

Problemi correlati