*Je suis un novice en Java/programmation.

J'ai écrit les exemples de code suivants pour essayer de comprendre mon problème, puis donner des exemples clairs de mon problème. Je souhaite que mon programme soit divisé en classes distinctes en fonction de leurs fonctions au sein de l'application : Main MainController - pilote l'interface graphique JavaFX. Initialiseur - validation sémantique des données saisies. Modèle - effectue des calculs sur les données et renvoie les résultats à MainController.

Là où je suis bloqué, c'est que l'initialiseur, en particulier le linkedhashmap et le premier contrôle de validation, lèvent une exception nullpointer. Cela n'arrive PAS si tout est dans une seule classe (j'ai commencé avec tout dans une classe, j'apprends maintenant à structurer dans des classes séparées, etc.). Cela n'arrive pas non plus si je n'utilise aucune fonctionnalité JavaFX.

Par exemple, cela fonctionne :

package helloWorld;
public class HelloWorld {

    public static void main(String[] args) {
        System.out.println("Hello world!");

        HashmapExample hashy = new HashmapExample();

        System.out.println(hashy.initialiser());
    }
}
package helloWorld;

import java.util.LinkedHashMap;
import java.util.Map;

public class HashmapExample {

    private Map<String,String> testMap = new LinkedHashMap<>();

    public void mapper() {

    testMap.put("one","a");
    testMap.put("two","b");
    testMap.put("three","c");

    }

    public String initialiser() {

        mapper();

        String errorField = testMap.entrySet().stream().filter(entry -> entry.getKey().isBlank()).map(Map.Entry::getValue).findFirst().orElse(null);

        String message;

        if (errorField != null) {
            return message = errorField + " blank!";
        } else {
            return message = "OK!";
        }

    }

}

Mais ce n'est pas le cas :

package helloWorldFX;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.layout.VBox;


public class Main extends Application {
    @Override
    public void start(Stage primaryStage) {
        try {
            VBox root = (VBox)FXMLLoader.load(getClass().getResource("MainGUI.fxml"));
            Scene scene = new Scene(root,400,400);
            scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
            primaryStage.setScene(scene);
            primaryStage.show();
        } catch(Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        launch(args);
    }
}
package helloWorldFX;

import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.TextArea;

public class MainController {

    @FXML private TextArea outputMessage;

    public void run(ActionEvent event) {

        Initialiser initialiser = new Initialiser();

        outputMessage.setText(initialiser.checker());

    }

}
package helloWorldFX;

import java.util.LinkedHashMap;
import java.util.Map;

import javafx.fxml.FXML;
import javafx.scene.control.TextField;

public class Initialiser {

    @FXML   private TextField one;
    @FXML   private TextField two;
    @FXML   private TextField three;

private Map<TextField,String> testMap = new LinkedHashMap<>();

    public void mapper() {

    testMap.put(one,"a");
    testMap.put(two,"b");
    testMap.put(three,"c");

    }

    public String checker() {

        mapper();

        String errorField = testMap.entrySet().stream().filter(entry -> entry.getKey().getText().isBlank()).map(Map.Entry::getValue).findFirst().orElse(null);

        String message;

        if (errorField != null) {
            return message = errorField + " blank!";
        } else {
            return message = "OK!";
        }

    }

}

Quand je fais une couverture en éclipse, je peux dire que c'est probablement causé par la ligne String errorField = testMap.entrySet().stream().filter(entry -> entry.getKey().isBlank()).map(Map.Entry::getValue).findFirst().orElse(null); mais je ne suis pas tout à fait sûr.

Je viens de remarquer dans le générateur de scènes que les champs "un", "deux" et "trois" n'apparaissent pas comme des ID valides pour les champs de texte, car ils ne sont pas dans la classe MainController. Cela doit avoir quelque chose à voir avec le problème, mais je ne comprends pas comment le résoudre.

Fichier FXML, comme demandé : n'oubliez pas qu'il s'agit d'un ensemble d'exemples de code, pas censé être un vrai programme, donc la mise en page est absurde.

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.Button?>
<?import javafx.scene.control.TextArea?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.VBox?>

<VBox prefHeight="300.0" prefWidth="300.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="helloWorldFX.MainController">
   <children>
      <TextField />
      <TextField />
      <TextField />
      <Button mnemonicParsing="false" onAction="#run" text="Button" />
      <TextArea fx:id="outputMessage" prefHeight="200.0" prefWidth="200.0" />
   </children>
</VBox>

Messages d'erreur :

Exception in thread "JavaFX Application Thread" java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
    at javafx.fxml/javafx.fxml.FXMLLoader$MethodHandler.invoke(FXMLLoader.java:1787)
    at javafx.fxml/javafx.fxml.FXMLLoader$ControllerMethodEventHandler.handle(FXMLLoader.java:1670)
    at javafx.base/com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86)
    at javafx.base/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
    at javafx.base/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
    at javafx.base/com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
    at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
    at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base/com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
    at javafx.base/com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:49)
    at javafx.base/javafx.event.Event.fireEvent(Event.java:198)
    at javafx.graphics/javafx.scene.Node.fireEvent(Node.java:8890)
    at javafx.controls/javafx.scene.control.Button.fire(Button.java:203)
    at javafx.controls/com.sun.javafx.scene.control.behavior.ButtonBehavior.mouseReleased(ButtonBehavior.java:206)
    at javafx.controls/com.sun.javafx.scene.control.inputmap.InputMap.handle(InputMap.java:274)
    at javafx.base/com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(CompositeEventHandler.java:218)
    at javafx.base/com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:80)
    at javafx.base/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
    at javafx.base/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
    at javafx.base/com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
    at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
    at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base/com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
    at javafx.base/com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54)
    at javafx.base/javafx.event.Event.fireEvent(Event.java:198)
    at javafx.graphics/javafx.scene.Scene$MouseHandler.process(Scene.java:3862)
    at javafx.graphics/javafx.scene.Scene.processMouseEvent(Scene.java:1849)
    at javafx.graphics/javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.java:2590)
    at javafx.graphics/com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:409)
    at javafx.graphics/com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:299)
    at java.base/java.security.AccessController.doPrivileged(AccessController.java:389)
    at javafx.graphics/com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleMouseEvent$2(GlassViewEventHandler.java:447)
    at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:411)
    at javafx.graphics/com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:446)
    at javafx.graphics/com.sun.glass.ui.View.handleMouseEvent(View.java:556)
    at javafx.graphics/com.sun.glass.ui.View.notifyMouse(View.java:942)
    at javafx.graphics/com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
    at javafx.graphics/com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(WinApplication.java:174)
    at java.base/java.lang.Thread.run(Thread.java:835)
Caused by: java.lang.reflect.InvocationTargetException
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:567)
    at com.sun.javafx.reflect.Trampoline.invoke(MethodUtil.java:76)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:567)
    at javafx.base/com.sun.javafx.reflect.MethodUtil.invoke(MethodUtil.java:273)
    at javafx.fxml/com.sun.javafx.fxml.MethodHelper.invoke(MethodHelper.java:83)
    at javafx.fxml/javafx.fxml.FXMLLoader$MethodHandler.invoke(FXMLLoader.java:1782)
    ... 46 more
Caused by: java.lang.NullPointerException
    at helloWorldFX.Initialiser.lambda$0(Initialiser.java:29)
    at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:176)
    at java.base/java.util.Spliterators$IteratorSpliterator.tryAdvance(Spliterators.java:1812)
    at java.base/java.util.stream.ReferencePipeline.forEachWithCancel(ReferencePipeline.java:127)
    at java.base/java.util.stream.AbstractPipeline.copyIntoWithCancel(AbstractPipeline.java:502)
    at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:488)
    at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474)
    at java.base/java.util.stream.FindOps$FindOp.evaluateSequential(FindOps.java:150)
    at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
    at java.base/java.util.stream.ReferencePipeline.findFirst(ReferencePipeline.java:543)
    at helloWorldFX.Initialiser.checker(Initialiser.java:29)
    at helloWorldFX.MainController.run(MainController.java:15)
    ... 58 more
2
Nexmo16 11 oct. 2019 à 06:17

1 réponse

Meilleure réponse

Solution:

Donc, sur la base des commentaires et d'une réflexion plus approfondie sur ce qui pourrait être la cause, j'ai trouvé cette solution de contournement.

J'instancier et remplis le Map dans le MainController, puis instancie la classe Initialiser, qui prend maintenant le Map comme paramètre via le constructeur. Ensuite, j'appelle la méthode checker à partir de l'objet Initialiser, qui exécute la vérification sémantique des données envoyées à l'objet lors de son instanciation.

Pour autant que je sache, le problème est qu'un fichier fxml ne peut traiter qu'une seule classe de contrôleur, vous ne pouvez donc pas remplir la carte avec TextFields de n'importe où, à l'exception de la classe de contrôleur principale. Si vous le faites, la méthode getText lancera nullpointerexception car elle pointe vers TextFields qui n'existe pas techniquement dans le fxml.

Voir le code modifié :

package helloWorldFX;

import java.util.LinkedHashMap;
import java.util.Map;

import javafx.fxml.FXML;
import javafx.scene.control.TextField;
import javafx.scene.control.TextArea;
import javafx.event.ActionEvent;

public class MainController {

    @FXML   private TextField one;
    @FXML   private TextField two;
    @FXML   private TextField three;

    @FXML   private TextArea outputMessage;

    public void run(ActionEvent event) {

        Map<TextField, String> testMap = new LinkedHashMap<>();

        testMap.put(one,"a");
        testMap.put(two,"b");
        testMap.put(three,"c");

        Initialiser initialiser = new Initialiser(testMap);

        outputMessage.setText(initialiser.checker());

    }

}
package helloWorldFX;

import java.util.Map;

import javafx.scene.control.TextField;

public class Initialiser {

    private Map<TextField, String> values;

    public Initialiser(Map<TextField, String> mappedValues) {
        this.values = mappedValues;
    }

    String message;

    public String checker() {

        String errorField = values.entrySet().stream().filter(entry -> entry.getKey().getText().isBlank()).map(Map.Entry::getValue).findFirst().orElse(null);

        if (errorField != null) {
            return message = errorField + " blank!";
        } else {
            return message = "OK!";
        }

    }

}

À ce stade, en raison de mon manque d'expérience, je ne sais pas s'il s'agit en fait d'une solution de contournement ou de la vraie façon correcte de le faire - peut-être que quelqu'un peut aider à remplir cette partie.

1
Nexmo16 11 oct. 2019 à 10:45