Search code examples
javajavafxhashmapfxml

HashMap with FXML JavaFX won't find my key


why can my Circle not be found or why is value null? I cannot find a solution to fix my HashMap or how to access the Circle in question.

import javafx.fxml.FXML;
import javafx.scene.input.MouseEvent;
import javafx.scene.shape.Circle;
import java.util.HashMap;
import java.util.Map;


public class Connect4 {

    @FXML
    private Circle A1;
    Map<String, Circle> circles = new HashMap<>() {{
        put("A1", A1);
    }};


    public void clickOnRectangleA(MouseEvent e) throws Exception {
        System.out.println(circles.containsValue(A1));
        circles.get("A1").setFill(SomeColor);
    }
}

My Console:

false
Exception in thread "JavaFX Application Thread" java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
    at javafx.fxml/javafx.fxml.FXMLLoader$MethodHandler.invoke(FXMLLoader.java:1862)
    at javafx.fxml/javafx.fxml.FXMLLoader$ControllerMethodEventHandler.handle(FXMLLoader.java:1729)
    at javafx.base/com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86)
    at javafx.base/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:234)
    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$ClickGenerator.postProcess(Scene.java:3563)
    at javafx.graphics/javafx.scene.Scene$MouseHandler.process(Scene.java:3865)
    at javafx.graphics/javafx.scene.Scene.processMouseEvent(Scene.java:1851)
    at javafx.graphics/javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.java:2584)
    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:391)
    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:412)
    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:831)
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:78)
    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 jdk.internal.reflect.GeneratedMethodAccessor2.invoke(Unknown Source)
    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:1857)
    ... 29 more
Caused by: java.lang.NullPointerException: Cannot invoke "javafx.scene.shape.Circle.setFill(javafx.scene.paint.Paint)" because the return value of "java.util.Map.get(Object)" is null
    at de.medieninformatik.prog2.Connect4.clickOnRectangleA(Connect4.java:190)
    ... 40 more

Solution

  • When the FXXMLLoader loads the FXML file, it

    1. Creates an instance of the controller class
    2. Injects any fields annotated @FXML with elements with matching fx:ids
    3. Calls the initialize() method, if there is one

    In your code, you call put("A1", A1) in the (anonymous) constructor of a HashMap subclass which is initialized inline. So this code is executed when the controller class is instantiated (step 1 above). This happens before A1 is injected, so at that time it is null. I.e. you are calling put("A1", null), which of course does not associate any value in the map with the key "A1".

    Instead, put the value in the map in the initialize() method:

    import javafx.fxml.FXML;
    import javafx.scene.input.MouseEvent;
    import javafx.scene.shape.Circle;
    import java.util.HashMap;
    import java.util.Map;
    
    
    public class Connect4 {
    
        @FXML
        private Circle a1;
    
        private Map<String, Circle> circles = new HashMap<>();
    
        @FXML
        private void initialize() {
            circles.put("A1", a1);
        }
    
    
        public void clickOnRectangleA(MouseEvent e) throws Exception {
            System.out.println(circles.containsValue(A1));
            circles.get("A1").setFill(SomeColor);
        }
    }