-5

I am trying to set the value of a String in another class and then get it from a third class. Now I am trying to do with set and get methods but I can't get it to work! When the user starts the program a button appear. When the button is pressed the test String get the value "Hello". And then I call the set class and put in test in the parameter. Now in the set method it is a System.out.println and it prints Hello, but when I am trying to get the method from my second class it prints null. I do not know why!Here is my classes:

public class Class1 {
    public static void main(String[] args) {
        Class3 c3 = new Class3();

        c3.setFilename("Hello");

        JFrame frame = new JFrame("Action Listener");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLayout(new FlowLayout());

        JButton button = new JButton("Click me!");
        frame.add(button);

        Class2 c2 = new Class2();

        button.addActionListener(c2);

        frame.setSize(300, 100);
        frame.setVisible(true);
    }
}

Here is my second class, it is the filename String I want to have to the next class:

public class Class2 implements ActionListener {

    String filename;

    public void actionPerformed(ActionEvent e) {
        Class3 c3 = new Class3();

        filename = c3.getFilename();

        System.out.println(filename); // Prints null
    }
}

Here is the third classs:

public class Class3 {
    String filename;

    public String getFilename() {
        return filename;
    }

    public void setFilename(String filename) {
        this.filename = filename;

        System.out.println(filename); // Prints Hello
    }
}
WeeRox
  • 124
  • 2
  • 13
  • 1
    What is not working? Have you tried debugging the code using your IDE's debugger tool? – Mohammad Najar Oct 14 '14 at 18:51
  • 5
    Too much code, too little context. – The Paramagnetic Croissant Oct 14 '14 at 18:52
  • @Mohammad Yes, I have tried debugging it, both with my IDE and with System.out.println and it returns null when it gets to the third class. – WeeRox Oct 14 '14 at 18:54
  • @Bennyz It returns null. – WeeRox Oct 14 '14 at 18:54
  • @WeeRox by looking at the number of down votes you should realize that your question is either unclear or too broad. Please narrow down the question and point out the areas you think the error is happening in. i.e. the exact line, the value you're getting, the output, etc. – Mohammad Najar Oct 14 '14 at 18:58
  • Your problem is not so much **how** to get the result held by a variable, since you already know how to do that -- use a getter method such as `getFileName()`. No, you're problem is more of **when** to get this result, in that you need to have one class **notified** when the state of one of its variables changes. To solve this, look into using a listener of some sort to allow Home to be notified when the fileName variable changes. An Observer Pattern works well here, and I've often implemented this using PropertyChangeListeners and PropertyChangeSupport. `... continued ....` – Hovercraft Full Of Eels Oct 14 '14 at 19:02
  • "When it gets to the third class" doesn't specify well enough when your null pointer appears. Classes are not executed, methods and statements are executed. Null pointers tend to produce exceptions and stacktraces; the stacktrace will indicate which method/s is/are in progress and on what line the error occurs. Putting one in a question is a good way to communicate where things are going wrong. – arcy Oct 14 '14 at 19:03
  • While posting question on Stack Overflow you are expected to post [minimal but full code which will let us reproduce your problem](http://sscce.org/). Your current code can't be used because it will not compile without `test` class. – Pshemo Oct 14 '14 at 19:04
  • `... continued ....` For example, please check out the code in [this answer](http://stackoverflow.com/a/4691447/522444). – Hovercraft Full Of Eels Oct 14 '14 at 19:05
  • @Pshemo Then I remove everything that has with that class to do. – WeeRox Oct 14 '14 at 19:08
  • Note that your fileName String variable in the homeButtonEvent class is null because you never assign it a value. – Hovercraft Full Of Eels Oct 14 '14 at 19:09
  • Could someone check my answer please. – rert588 Oct 14 '14 at 19:10
  • 1
    @WeeRox Not only that. You should remove from your example everything which has nothing to do with your problem. Consider starting from scratch and writing new example, most of the time this method is faster and safer than removing existing code. – Pshemo Oct 14 '14 at 19:12
  • This way you will not only get answer to your question faster, but you will attract more people to answer your questions. Think about it, if you would see 3 questions: two short ones, with simple code example and one long one, with overcomplicated example which one would you try to answer first? – Pshemo Oct 14 '14 at 19:16
  • Could someone check if my answer is valid – rert588 Oct 14 '14 at 19:18
  • @rert588: I have checked, and no, you're answer is not valid. – Hovercraft Full Of Eels Oct 14 '14 at 19:49
  • To the original poster: you've got name clashes going on including a JButton variable named train and a class named train. For your sake and especially for ours, **don't do that!** Also understand that every time you create a new Train(...)` object, you're doing just that, creating a new one, and changing the state of the new one will have **no effect** on other objects of the same class. That's probably one of your biggest problems. – Hovercraft Full Of Eels Oct 14 '14 at 19:51
  • @Pshemo I this better? – WeeRox Oct 16 '14 at 19:04
  • @WeeRox Soooo much better. +1 for improvement. – Pshemo Oct 16 '14 at 19:22
  • "*but when I am trying to get the method from my second class it prints null. I do not know why!*" Notice that at start of `main` method you are creating new `Class3` instance on which you are invoking `setFilename` method which prints "hello", but you are not using this instance anywhere. Instead in Class2 `actionPerformed` method you are creating new (separate) instance of `Class3` and you don't pass any `filename` to this new instance so it stays `null`. Wouldn't it be better to pass in `Class2` constructor instance of `Class3`, store it in field, and reuse it in `actionPerformed`? – Pshemo Oct 16 '14 at 19:36
  • @Pshemo Do you mean I need to put the set and get methods in Class2 and delete Class3? – WeeRox Oct 17 '14 at 06:54
  • Not necessarily. In most cases it is good to have data split in few classes. I mean instead of creating new instances of Class3 each time button will be pressed, you could reuse one you created earlier. You just need to pass it in Class2 constructor, store it in some Class2 field, and reuse in `actionPerformed` method. I mean something like this: http://pastebin.com/u7GGz1Ns – Pshemo Oct 17 '14 at 12:34

1 Answers1

2

Your problems include:

  • You're never setting the fileName of the train class (check it -- where do you ever call fileName = something?)
  • You're creating new train objects and expecting that the new object will be able to sense changes made to other train objects. Java doesn't work that way. You need to test the same object that you change.
  • Your program naming conventions are confusing and dangerous. Remember that class names begin with an upper letter. Don't give variables the same names as unrelated class, such as your train class and your train button.
  • Your program has a lot of unnecessary complexity such as parallel arrays that make debugging and enhancing difficult.
  • You're swapping contents of a JPanel instead of using the much easier to use CardLayout which will do the swapping for you.

For example, you could encapsulate your JButton string names and file names in a class or enum. The advantage of using a class, is then you could read in the data from a file and create objects of the class from the file data, giving extra flexibility, however for purposes of my example, and to avoid adding extra files, I'll use an enum, something like

public enum ButtonFileName {
   MONDAY("Monday", KeyEvent.VK_M, "monday_blues.jpg"), 
   TUESDAY("Tuesday", KeyEvent.VK_T, "tuesday_fun.jpg"), 
   WEDNESDAY("Wednesday", KeyEvent.VK_W, "humpday.jpg"), 
   THURSDAY("Thursday", KeyEvent.VK_H, "almost_friday.jpg");

   private String buttonText;
   private String fileName;
   private int mnemonic;

   private ButtonFileName(String buttonText, int mnemonic, String fileName) {
      this.buttonText = buttonText;
      this.mnemonic = mnemonic;
      this.fileName = fileName;
   }

   public String getButtonText() {
      return buttonText;
   }

   public int getMnemonic() {
      return mnemonic;
   }

   public String getFileName() {
      return fileName;
   }
}

Then I can give my main class a ButtonFileName variable, selectedButtonFileName, and then fill that variable if a button is pressed. I can also create my own Actions, one for each ButtonFileName enum, and use these Actions to set my buttons. For example:

import java.awt.CardLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.*;

@SuppressWarnings("serial")
public class Home2 extends JPanel {
   private static final String TRAIN_PANEL = "train panel";
   private static final String BTN_ROW_PANEL = "btn row panel";
   private CardLayout cardLayout = new CardLayout();
   private ButtonFileName selectedButtonFileName = null;
   private JPanel buttonRowPanel = new JPanel();
   private JPanel trainPanel = new JPanel();

   public Home2() {
      buttonRowPanel = createButtonRowPanel();
      trainPanel = createTrainPanel();

      setLayout(cardLayout);
      add(buttonRowPanel, buttonRowPanel.getName());
      add(trainPanel, trainPanel.getName());
   }

   private JPanel createButtonRowPanel() {
      JPanel btnRowPanel = new JPanel(new GridLayout(1, 0, 5, 5));
      btnRowPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
      btnRowPanel.setName(BTN_ROW_PANEL);
      for (ButtonFileName btnFileName : ButtonFileName.values()) {
         JButton btn = new JButton(new RowButtonAction(btnFileName, this));
         btnRowPanel.add(btn);
      }
      return btnRowPanel;
   }

   private JPanel createTrainPanel() {
      JPanel trainPanel = new JPanel();
      trainPanel.setName(TRAIN_PANEL);
      JButton trainBtn = new JButton(new TrainAction("Train", KeyEvent.VK_T, this));
      trainPanel.add(trainBtn);
      return trainPanel;
   }

   public ButtonFileName getSelectedButtonFileName() {
      return selectedButtonFileName;
   }

   public void setSelectedButtonFileName(ButtonFileName selectedButtonFileName) {
      this.selectedButtonFileName = selectedButtonFileName;
   }

   public void nextCardLayoutView() {
      cardLayout.next(this);
   }

   private static void createAndShowGui() {
      JFrame frame = new JFrame("Home2");
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.getContentPane().add(new Home2());
      frame.pack();
      frame.setLocationRelativeTo(null);
      frame.setVisible(true);
   }

   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            createAndShowGui();
         }
      });
   }

}

Again the enum

enum ButtonFileName {
   MONDAY("Monday", KeyEvent.VK_M, "monday_blues.jpg"), 
   TUESDAY("Tuesday", KeyEvent.VK_T, "tuesday_fun.jpg"), 
   WEDNESDAY("Wednesday", KeyEvent.VK_W, "humpday.jpg"), 
   THURSDAY("Thursday", KeyEvent.VK_H, "almost_friday.jpg");

   private String buttonText;
   private String fileName;
   private int mnemonic;

   private ButtonFileName(String buttonText, int mnemonic, String fileName) {
      this.buttonText = buttonText;
      this.mnemonic = mnemonic;
      this.fileName = fileName;
   }

   public String getButtonText() {
      return buttonText;
   }

   public int getMnemonic() {
      return mnemonic;
   }

   public String getFileName() {
      return fileName;
   }
}

My Action for the train JButton

import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;

@SuppressWarnings("serial")
public class TrainAction extends AbstractAction {
   private Home2 home;

   public TrainAction(String name, int mnemonic, Home2 home) {
      super(name);
      putValue(MNEMONIC_KEY, mnemonic);
      this.home = home;
   }

   @Override
   public void actionPerformed(ActionEvent e) {
      System.out.println("Selected File Name: "
            + home.getSelectedButtonFileName().getFileName());
      home.nextCardLayoutView();
   }
}

My Action for the row buttons

import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;

@SuppressWarnings("serial")
public class RowButtonAction extends AbstractAction {
   private ButtonFileName btnFileName;
   private Home2 home;

   public RowButtonAction(ButtonFileName btnFileName, Home2 home) {
      super(btnFileName.getButtonText());
      putValue(MNEMONIC_KEY, btnFileName.getMnemonic());
      this.btnFileName = btnFileName;
      this.home = home;
   }

   @Override
   public void actionPerformed(ActionEvent e) {
      home.setSelectedButtonFileName(btnFileName);
      home.nextCardLayoutView();
   }
}

As for flexibility, say I wanted to add another JButton and file name. Add a Friday element to the enum and it's done.

Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373