Search code examples
javaclassstack-overflow

Declaring Classes causes StackOverflow error


I was looking around for answers and the closest I saw to my problem was here: Object creation causes runtime error which throws java.lang.StackOverflowError just like mine. It refers to my lines where I initialize my classes as objects and I don't think it should be looping. Here's the error:

Exception in Application start method
java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.sun.javafx.application.LauncherImpl.launchApplicationWithArgs(LauncherImpl.java:389)
at com.sun.javafx.application.LauncherImpl.launchApplication(LauncherImpl.java:328)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at sun.launcher.LauncherHelper$FXHelper.main(LauncherHelper.java:767)
Caused by: java.lang.RuntimeException: Exception in Application start method
at com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:917)
at com.sun.javafx.application.LauncherImpl.lambda$launchApplication$154(LauncherImpl.java:182)
at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.StackOverflowError
at sample.Script.<init>(Script.java:9)
at sample.Controller.<init>(Controller.java:34)
at sample.Script.<init>(Script.java:9)
at sample.Controller.<init>(Controller.java:34)
at sample.Script.<init>(Script.java:9)
at sample.Controller.<init>(Controller.java:34)
at sample.Script.<init>(Script.java:9)
at sample.Controller.<init>(Controller.java:34)
at sample.Script.<init>(Script.java:9)

Here's Controller up to line 34:

package sample;

import javax.swing.*;
import javafx.fxml.FXML;
import java.util.Arrays;
import javafx.geometry.Insets;
import javafx.scene.layout.VBox;
import javafx.scene.control.Label;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.AnchorPane;
import javafx.scene.control.TextField;
import javafx.scene.control.ScrollPane;

public class Controller {

    private String msg;
    private char[] msgA;
    @FXML
    private Label opt1;
    @FXML
    private Label opt2;
    @FXML
    private Label opt3;
    @FXML
    private TextField msgBox;
    @FXML
    private AnchorPane optPane;
    @FXML
    private AnchorPane gameUI;
    @FXML
    private AnchorPane mainMenu;
    @FXML
    private ScrollPane labelPane;
    private final Script script = new Script(); //Line 34 <--
    ....
}

And here's Script up to line 9:

package sample;

import java.util.ArrayList;

class Script {

    /*Variables*/
    public final ArrayList choice = new ArrayList();
    private final Controller c = new Controller();//Line 9! <--
    ....
}

Is it because I'm creating the objects in both classes? Should I extend Controller?


Solution

  • public class Controller {
        private final Script script = new Script();
        ...
    }
    

    Every instance of Controller creates a new instance of Script.

    public class Script {
        private final Controller c = new Controller();
        ...
    }
    

    Every instance of Script creates a new instance of Controller.

    That means instantiating either class would leave you stuck in a loop: Say you created a new instance of Script, imagine the stacktrace looked something like this:

    1. During initialization of the Script, an instance of Controller is created.
    2. During the initialization of the Controller, an instance of Script is created.
    3. During initialization of the Script, an instance of Controller is created.
    4. During the initialization of the Controller, an instance of Script is created.

    And so forth.

    What you are trying to achieve is mutual class references. Controller holds a reference to Script and Script holds a reference to Controller. Disregarding whether this is appropriate in this context, an approach to achieving this would be to pass a reference to the Controller to the constructor of Script.

    public class Controller {
        private final Script script;
        public Controller() {
            script = new Script(this);
        }
        public Script getScript() { 
            return script; 
        }
        ...
    }
    
    public class Script {
        private final Controller controller;
        public Script(Controller c) {
            controller = c;
        }
        ...
    }