Search code examples
javaswingjframestack-overflow

StackOverFlow error keeps on propping up even without recursion in my code


I keep on seeing the "Exception in thread "main" java.lang.StackOverflowError". I know this mainly pops up because of recursion however in my code I don't see how its being recursive. My main issue is that I really don't know how to fix this error.

I have three classes that I am using. I am using a main class called "mainIA" which I use to execute the code, then I have another class called "MainFrame" which extends "mainIA". Then I have "ServingButtons" class which extends "MainFrame" Class.

Here is my mainIA class code:

 public static void main(String[] args) {
        MainFrame frame = new MainFrame();
    }

Here is my MainFrame class code (this is where the error is coming from.

package sweets;

import javax.swing.*;
import java.awt.*;

public class MainFrame extends JFrame {
    //all the foods
    String[] food = {bananaBread, brownies, chocolateChipcookies, macarons};
    final static String bananaBread = "banana bread";
    final static String brownies = "brownies";
    final static String chocolateChipcookies = "chocolate chip cookies";
    final static String macarons = "macarons";

    public MainFrame(){

        JPanel recipeName = new JPanel(); //this will have the JComboBox with the foodList
        JLabel recipe = new JLabel("Recipes: ");
        recipeName.setLayout(new FlowLayout());
        recipeName.add(recipe);
        JComboBox foodList = new JComboBox(food);
        recipeName.add(foodList);

        JLabel ingredients = new JLabel("Ingredients"); //this will have the dynamically changing ingredients
        JLabel inputRecipe = new JLabel("Input Recipe"); //this will have the input recipe JTextField


        this.setTitle("Recipe Portion Calculator");
        this.setLayout(new GridLayout(4,1));
        this.setSize(800,800);
        this.add(recipeName);
        this.add(new ServingButtons()); //<< IntelliJ identifies the error on this line
        this.add(ingredients);
        this.add(inputRecipe);
        this.setDefaultCloseOperation(EXIT_ON_CLOSE);

        this.setVisible(true);
    }
}

Here is my ServingsButton class code:

package sweets;

import javax.swing.*;
import java.awt.*;

public class ServingButtons extends MainFrame {
    public int servingNumber = 0;
    //ingredient Volumes
    public int almondFlourVol = 0;
    public int allPurposeFlourVol = 0;
    public int unsaltedButterVol = 0;
    public int bakingSodaVol = 0;
    public int bakingPowderVol = 0;
    public int vanillaExtractVol = 0;
    public int cocoaPowderVol = 0;
    public int saltVol = 0;
    public int chocolateChipsVol = 0;
    public int instantEspressoVol = 0;
    public int vegetableOilVol = 0;
    public int bananasVol = 0;
    //sugars
    public int granulatedSugarVol = 0;
    public int powderedSugarVol = 0;
    public int lightBrownSugarVol = 0;
    //eggs
    public int eggsVol = 0;
    public int eggWhitesVol = 0;

    JLabel granulatedSugar = new JLabel("Granulated Sugar: " + (granulatedSugarVol));
    JLabel powderedSugar = new JLabel("Powdered Sugar: " + (powderedSugarVol));
    JLabel lightBrownSugar = new JLabel("Light Brown Sugar: " + (lightBrownSugarVol));
    JLabel eggs = new JLabel("Eggs: " + (eggsVol));
    JLabel eggWhites = new JLabel("Egg Whites: " + (eggWhitesVol));
    JLabel bananas = new JLabel("Bananas: " + (bananasVol));
    JLabel vegetableOil = new JLabel("Vegetable Oil: " + (vegetableOilVol));
    JLabel instantEspresso = new JLabel("Instant Espresso: " + (instantEspressoVol));
    JLabel chocolateChips = new JLabel("Chocolate Chips: " + (chocolateChipsVol));
    JLabel salt = new JLabel("Salt: " + (saltVol));
    JLabel cocoaPowder = new JLabel("Cocoa Powder: " + (cocoaPowderVol));
    JLabel vanillaExtract = new JLabel("Vanilla Extract: " + (vanillaExtractVol));
    JLabel bakingSoda = new JLabel("Baking Soda: " + (bakingSodaVol));
    JLabel unsaltedButter = new JLabel("Unsalted Butter: " + (unsaltedButterVol));
    JLabel allPurposeFlour = new JLabel("All-Purpose Flour: " + (allPurposeFlourVol));
    JLabel almondFlour = new JLabel("Almond Flour: " + (almondFlourVol));
    JLabel bakingPowder = new JLabel("Baking Powder: " + (bakingPowderVol));
    JPanel servings = new JPanel();

    public ServingButtons() {
        servings.setLayout(new GridLayout(1, 4));
        //used this code from this Quora Link: https://www.quora.com/How-would-I-get-my-program-to-add-1-every-time-I-press-the-button-%E2%80%9Da%E2%80%9D-in-Java
        servings.add(new JLabel("Number of Servings: "));
        JLabel counterLbl = new JLabel(Integer.toString(servingNumber));
        JButton decButton = new JButton(("-"));
        decButton.addActionListener(l -> {
            counterLbl.setText(Integer.toString(--servingNumber));
            almondFlour.setText("Almound Flour: " + (--almondFlourVol));
            granulatedSugar.setText("Granulated Sugar: " + (--granulatedSugarVol));
            powderedSugar.setText("Powdered Sugar: " + (--powderedSugarVol));
            lightBrownSugar.setText("Light Brown Sugar: " +(--lightBrownSugarVol));
            eggs.setText("Eggs: " + (--eggsVol));
            eggWhites.setText("Egg Whites: " + (--eggWhitesVol));
            bananas.setText("Bananas: " + (--bananasVol));
            vegetableOil.setText("Vegetable Oil: " + (--vegetableOilVol));
            instantEspresso.setText("Instant Espresso: " + (--instantEspressoVol));
            chocolateChips.setText("Chocolate Chips: " + (--chocolateChipsVol));
            salt.setText("Salt: " + (--saltVol));
            cocoaPowder.setText("Cocoa Powder: " + (--cocoaPowderVol));
            vanillaExtract.setText("Vanilla Extract: " + (--vanillaExtractVol));
            bakingSoda.setText("Baking Soda: " + (--bakingSodaVol));
            unsaltedButter.setText("Unsalted Butter: " + (--unsaltedButterVol));
            allPurposeFlour.setText("All-Purpose Flour: " + (--allPurposeFlourVol));
            bakingPowder.setText("All-Purpose Flour: " + (--bakingPowderVol));
        });
        JLabel space = new JLabel("");
        servings.add(space);
        servings.add(decButton);
        servings.add(counterLbl);
        JButton incButton = new JButton("+");
        incButton.addActionListener(l -> {
            counterLbl.setText(Integer.toString(++servingNumber));
            granulatedSugar.setText("Granulated Sugar: " + (++granulatedSugarVol));
            powderedSugar.setText("Powdered Sugar: " + (++powderedSugarVol));
            lightBrownSugar.setText("Light Brown Sugar: " + (++lightBrownSugarVol));
            eggs.setText("Eggs: " + (++eggsVol));
            eggWhites.setText("Egg Whites: " + (++eggWhitesVol));
            bananas.setText("bananas: " + (++bananasVol));
            vegetableOil.setText("Vegetable Oil: "+ (++vegetableOilVol));
            instantEspresso.setText("instant Espresso: " + (++instantEspressoVol));
            chocolateChips.setText("Chocolate Chips: "+ (++chocolateChipsVol));
            salt.setText("Salt: " + (++saltVol));
            cocoaPowder.setText("Cocoa Powder: " + (++cocoaPowderVol));
            vanillaExtract.setText("Vanilla Extract: " + (++vanillaExtractVol));
            bakingSoda.setText("Baking Soda: " + (++bakingSodaVol));
            unsaltedButter.setText("Unsalted Buttter: " + (++unsaltedButterVol));
            allPurposeFlour.setText("All-Purpose Flour: " + (++allPurposeFlourVol));
            almondFlour.setText("Almound Flour: " + (++almondFlourVol));
            bakingPowder.setText("All-Purpose Flour: " + (++bakingPowderVol));
        });
        servings.add(incButton);
    }
}

Any help would be greatly appreciated!


Solution

  • new ServingButtons() does a lot:

    • First, we determine the full class hierarchy. Which is Object -> .... JFrame -> MainFrame -> ServingButtons.

    • Then, going from Object up to ServingButtons, the JVM will run the instance initializers (here, all non-static fields that are initialized as they are declared by some non-constant value, such as JLabel granulatedSugar = new JLabel("Granulated Sugar: " + (granulatedSugarVol));).

    • Then, the ServingButtons constructor is invoked.

    • Note that all constructors MUST start with a this() or super() statement. (in JDK22+ you no longer have to run it as first thing, but you still have to guaranteed run a this or super). That means any given constructor necessarily ends up invoking one of its parent class's constructor, and so on.

    Thus, constructing a new ServingButtons, given that it extends MainFrame, it'll end up running MainFrame's constructor, which calls new ServingButtons(), which ends up running MainFrame's constructor, which calls new ServingButtons(), .... you get the point.

    ServingButtons having extends MainFrame on it is weird. I don't know what you thought that would accomplish, but, it doesn't - and it does cause that stack overflow.