Cannot access class instance methods outside main method - java

I'm working through the book "Head first Java" and started chapter 12.
When I tried to create the method changeButtonText() I cannot access any of the class methods from button.
Why is this? What have I done wrong in this code?
import javax.swing.*;
public class Main {
public static void main(String[] args) {
JFrame frame = new JFrame();
JButton button = new JButton("Click Me");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(button);
frame.setSize(300,300);
frame.setVisible(true);
}
public void changeButtonText(){
button.setText("I've been clicked");
}
}

The reason you cannot access the variable is because of its scope (tutorial about the scope of variables: https://www.baeldung.com/java-variable-scope).
You declare the variable JButton button in the main method, therefore it is not accessible anywhere outside of it, even in a method that main calls itself.
To make changeButtonText aware that the button variable exists, you have to pass it as parameters of this method:
import javax.swing.*;
public class Main {
public static void main(String[] args) {
JButton button = new JButton("Click Me");
changeButtonText(button);
}
public static void changeButtonText(JButton button){
button.setText("I've been clicked");
}
}
I also added the static keyword in front of the changeButtonText method, because main is also a static method. Check this link for example to have more details about the difference: https://www.geeksforgeeks.org/static-methods-vs-instance-methods-java/

Surely this is a problem of scope and it can be overcome with defining variable at global level and initialize wherever you required. something like -
public class Main {
private static JButton button;
public static void main(String[] args) {
JFrame frame = new JFrame();
button = new JButton("Click Me");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(button);
frame.setSize(300,300);
frame.setVisible(true);
Main.changeButtonText();
}
public static void changeButtonText(){
button.setText("I've been clicked");
}
}

There's a couple of things that is wrong with your code and concepts that you seem to not fully grasp. These concepts are this. and scopes.
public void changeButtonText(){
button.setText("I've been clicked");
}
First we need to talk about scopes. A scope is what's declared between { /* here */ } i.e. two curly brackets. From within a scope you can access and declare both local and global objects, variables, members etc.
So here's an example using local scope (what you did):
public void myFunction(){
JButton button = new Jbutton();
}
If I then go ahead and try to access the button outside of the scope, it wont know what button is. Since it's not declared within that local scope.
public void myFunction(){
JButton button = new Jbutton();
}
public static void main(String[] args) {
button.setText("hello"); //error
// compiler thinks: what's `button` referring to here? its not declared in this scope.
}
Global class scope:
JButton button = new JButton(); //declaring button in global class scope
public void myFunction(){
this.button.setText("hello"); // will access the globally declared variable, object, member called `button`.
//^ notice the usage of `this`.
//`this` will look outside of the local functions scope for publically declared class-members.
}
To fix your particular problem, you could either make your function static and pass in a JButton object, or you could declare your button in the global class scope like this:
public class Main {
JButton button = new JButton("Click Me"); //global class scope
public static void main(String[] args) { //local scope of main start {
Main myClass = new Main(); //instantiate class
myClass.changeButtonText(); //access class-member function
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(this.button); // this.
frame.setSize(300,300);
frame.setVisible(true);
} // local scope of main ends }
public void changeButtonText(){
this.button.setText("I've been clicked"); //access global class members using `this` keyword
}
}

Related

Why this function for JFrame is executed twice?

Introduction
I'm learning how to present GUIs for user 'friendly' input. I will reference below some web pages for those who are interested in the matter.
Code
public class TestGUI{
private JFrame mainFrame;
private JLabel headerLabel;
private JLabel statusLabel;
private JPanel controlPanel;
public TestGUI()
{
prepareGUI();
}
private void prepareGUI()
{
mainFrame = new JFrame("TestGUI"); //Header name
mainFrame.setSize(420, 320); //Size of the frame
mainFrame.setLayout(new GridLayout(3, 1)); //??
mainFrame.addWindowListener(new WindowAdapter() //Waits for an user event
{
//When the frame is closes, the program does too.
#Override
public void windowClosing(WindowEvent windowEvent)
{
System.exit(0); //Exit program
}
});
mainFrame.setVisible(true);//GUI is visible
}
public static void main(String[] args) {
TestGUI test = new TestGUI(); //constructor
test.prepareGUI(); //Call the method
}
}
Problem
While running the code I saw that 2 identical frames pop up. I went to debug it and saw that it is executed twice when I call the method!
Why is that?
I only called it once with testGUI.prepareGUI(); in the main function.
Webpages for learning basic GUI in Java
JavaFX
GUI Programming with AWT
You call prepareGUI() in the constructor as well.
public TestGUI()
{
prepareGUI();
}
When you call new TestGUI(), this constructor gets called and so does the function.
You are calling prepareGui twice
Once here
public TestGUI()
{
prepareGUI();
}
and once here
TestGUI test = new TestGUI(); //constructor
test.prepareGUI(); //Call the method
so first block is executed on new TestGUI() call
You are calling the prepareGui() method twice. One in your constructor and once on your created object (in the main method)
The issue is that constructor (that is TestGUI()) you are already calling prepareGUI(). so just omit the other call to prepareGUI(), which is test.prepareGUI().

Inner Class Instantiate Clarification

I don't understand a feature of the inner class CHameleon. I don't get what line 8 in theory means.
I think it means to produce a duplicate version of JFrame that will be accessed outside of the inner class, in order to manipulate the intended JFrame object.
Edit: Code brings a null pointer exception error because JFrame object is never referenced.
Solution: modify JFrame frame to final JFrame frame.
This brings up the question on what if there were multiple JFrames?
For example, if I had a class garden with different veggies, and I created an inner class colorsplat that colors these veggies. Is the only solution to create specific classes that target specfic veggies? So to answer my own question, in the case of multiple JFrames, they would appear as different kinds of classes, my case?
public class LabelsButtonsPanelsandSnakes {
public static void main(String[] args){
final JFrame frame = new JFrame("Test");
JMenuBar menuBar = new JMenuBar(); //menubar
JMenu menu = new JMenu("Menu");
JMenuItem chameleon = new JMenuItem("Change Color");
class CHameleonaction implements ActionListener{ //inside class opens
JFrame frameHolder; //line 8
public void actionPerformed(ActionEvent e)
{
frame.getContentPane().setBackground(new Color(112,253,95));
}
} //inside class ends
chameleon.addActionListener(new CHameleonaction());
menuBar.add(menu);
frame.setJMenuBar(menuBar);
}
You're doing way too much within your main method, and most of this code belongs elsewhere since the main method should be used to mainly create your main objects and start them running, but little else. As noted in my comment, your current code looks to lead you to a NullPointerException since you're trying to call a method on a field that never appears to have been initialized. I'm OK with your using an inner class for simple listener interfaces, and as noted, an anonymous inner class would work fine, but you must do this with care. If you need to refer to an outer class variable you have a few options:
If the outer variable is a field rather than a local variable, the inner class can directly reference it.
If it is a local variable, it must be declared final.
Most Swing listeners have an XxxEvent parameter that returns the source of the event via getSource() and this can often lead you to a non-inner class reference.
For example
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import javax.swing.*;
public class Foo2 extends JPanel {
private static final Color NEW_COLOR = new Color(112,253,95);
private static final int PREF_W = 400;
private static final int PREF_H = PREF_W;
private JMenuBar menuBar = new JMenuBar();
public Foo2() {
JMenuItem chameleon = new JMenuItem(new ChangeColorAction("Change Color"));
JMenu menu = new JMenu("Menu");
menu.add(chameleon);
menuBar.add(menu);
}
public JMenuBar getMenuBar() {
return menuBar;
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
private class ChangeColorAction extends AbstractAction {
public ChangeColorAction(String name) {
super(name);
}
#Override
public void actionPerformed(ActionEvent e) {
setBackground(NEW_COLOR);
}
}
private static void createAndShowGui() {
Foo2 mainPanel = new Foo2();
JFrame frame = new JFrame("Foo2");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.setJMenuBar(mainPanel.getMenuBar());
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}

Button Function Not Working?

So I am trying to write a program using swing components so whenever the user clicks on a button, a different word shows up in the text box. I keep getting an error saying "non static method buttons() cannot be referenced from a static context" I checked other answers on how to fix it with the same error, but I still don't understand it. it appears here:
color.buttons();
And this is my code:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class color {
private JFrame f;
private JLabel label;
private JPanel controlPanel;
private JButton button1;
private JButton button2;
private JButton button3;
private JTextField textbox;
public color() {
prepareGUI();
}
public static void main(String args[]) {
color c = new color();
color.buttons();
}
private void prepareGUI() {
f = new JFrame("Colors");
f.setSize(400, 400);
f.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent windowEvent) {
System.exit(0);
}
});
label = new JLabel("", JLabel.CENTER);
label.setBounds(20, 105, 200, 25);
textbox = new JTextField("", JTextField.CENTER);
textbox.setBounds(20, 75, 125, 50);
f.add(label);
f.add(textbox);
f.setVisible(true);
}
private void buttons() {
label.setText("Click a Button to Reveal Text");
textbox.setText("Which Color?");
JButton button1 = new JButton("Blue");
button1.setBounds(10, 305, 120, 75);
JButton button2 = new JButton("Red");
button2.setBounds(140, 305, 120, 75);
JButton button3 = new JButton("Yellow");
button3.setBounds(270, 305, 120, 75);
button1.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
textbox.setText("Blue");
}
});
button2.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
textbox.setText("Red");
}
});
button3.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
textbox.setText("Yellow");
}
});
controlPanel.add(button1);
controlPanel.add(button2);
controlPanel.add(button3);
f.setVisible(true);
}
}
You should try c.buttons(); instead of color.buttons();
As non static methods can not be directly referenced, you need an object to do that.
Where as if the method is static then you can use color.buttons();.
The static declaration is used when a field is associated with a class, rather than with any particular object. Now, let's consider your code:
public static void main(String args[]) {
color c = new color();
color.buttons();
}
Of course, the main method has to be static in order to initially run your code. By extension, everything within your static main method must also be static or in other words must be able to run without being directly associated with any object. Therefore, you cannot call color.buttons() since the method is not a static method.
You have two solutions:
Make the method static. Of course, it might not actually make sense to make it static in this case but sometimes in other cases it might be appropriate.
Call the method in the new, instantiated color object, c, as such: c.buttons(); OR call buttons() inside the constructor color().
For clarity, the second solution, in your code, would look like the following:
public color() {
prepareGUI();
buttons();
}
OR
public static void main(String args[]) {
color c = new color();
c.buttons();
}
Calling c.buttons() inside of the static main method is possible since the buttons() method is a method associated with the instantiated object, c.
Finally, some best practices and tips:
You should read the Java Naming Conventions and apply it to your code. For example, your class should be capitalized to be Color instead of color. Following naming conventions makes it easier for other people to read your code because naming conventions are a sort of contract that developers agree on.
You should not call Java Swing methods outside of the Event Dispatch Thread (EDT) as most Swing methods are not thread-safe. Calling Swing methods outside the EDT may result in hard-to-find problems later on.
You can't call a non-static method from a static method in java.
In your case, you are trying to call method buttons() which is a non-static method, where as main method (where you are calling buttons method) is a static method.
Change your main method like this.
public static void main (String args[]){
color c = new color();
c.buttons();
}
In this way you are calling the member function of an object.

Ability to access a Text Area from a different class

How would i be able to run this function from my main() to build the gui, and then use code from elsewhere to handle the button click and retrieve input from the text field?
package main;
import javax.swing.*;
import java.awt.*;
public class Gui {
public static void mainGUI() {
UIManager.put("swing.boldMetal", Boolean.FALSE);
java.net.URL imgApp = ClassLoader.getSystemResource("res/app.png");
JFrame mainWin = new JFrame("jIRC");
mainWin.setSize(1024, 720);
mainWin.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainWin.setIconImage(new ImageIcon(imgApp).getImage());
Container panel = mainWin.getContentPane();
panel.setLayout(null);
JTextArea inputBox = new JTextArea();
inputBox.setSize(300, 100);
inputBox.setLocation(500, 250);
JButton sendButton = new JButton();
sendButton.setText("Send");
sendButton.setFont(new Font("Helvetica", Font.ITALIC, 16));
sendButton.setSize(72, 32);
sendButton.setLocation(500, 500);
panel.add(inputBox);
panel.add(sendButton);
mainWin.setVisible(true);
}
}
Here's my class with the main function:
public class Run{
public static void main(String[] args) {
main.Debug.startupDebug();
main.Gui.mainGUI();
}
}
How would I go about placing some of my code in a non-static field?
You've got everything in a static method, and that won't allow you to use any of the power of object-oriented programming. Consider creating OOP-compliant classes non-static fields and methods and with public getter and setter methods, and this way other classes can affect the behavior of this class.
Edit
You posted:
public class Run{
public static void main(String[] args) {
main.Debug.startupDebug();
main.Gui.mainGUI();
}
}
But what you need to do instead is something like:
public class Run{
public static void main(String[] args) {
GUI gui = new GUI();
Debug debug = new Debug();
debug.setGui(gui);
gui.setDebug(debug);
gui.startGui();
}
}
Or something similar. Again avoid using static anything.

Java: Using an actionlistener to call a function in another class on an object from that class

Basically what I want to do is get a start button to initiate a method running in another class and acting on another object.
My code for the listener:
button1a.addActionListener(new ActionListener() {
public void actionPerformed (ActionEvent event) {
// Figure out how to make this work
//sim.runCastleCrash();
}
} );
My code for the other class:
public static void main(String[] args) {
CastleCrash sim;
sim = new CastleCrash();
}
and
public void runCastleCrash() {
System.out.println("Castle Crash is beginning...");
//Other method parts here to be added
}
I get the feeling this can't be too hard, but I'm missing a piece.
One way to reference things in an anonymous class is using the final keyword:
public static void main(String[] args) {
final Object thingIWantToUse = "Hello";
JButton button = new JButton("Click");
button.addActionListener(new ActionListener() {
#Override public void actionPerformed(ActionEvent e) {
System.out.println(thingIWantToUse);
}
});
JFrame frame = new JFrame();
frame.setLayout(new FlowLayout());
frame.add(button);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
Alternatively, you can access members (variables or methods) of an enclosing type:
public class ActionListenerDemo2 {
private final JFrame frame = new JFrame();
private Object thingIWantToUse = "Hello";
public ActionListenerDemo2() {
JButton button = new JButton("Click");
button.addActionListener(new ActionListener() {
#Override public void actionPerformed(ActionEvent e) {
thingIWantToUse = "Goodbye";
System.out.println(thingIWantToUse);
}
});
frame.setLayout(new FlowLayout());
frame.add(button);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
new ActionListenerDemo2().frame.setVisible(true);
}
}
I've had the same problem like you and this is how i solved it.
You can either make your object final (final CastleCrash sim = new CastleCrash();), but i didn't want to do that, or you can make something like a setter method to run the method in your other class:
My code for the listener class:
button1a.addActionListener(new ActionListener()
{
public void actionPerformed (ActionEvent event)
{
//How to make this work ?
//Like this:
runCC();
}
});
public void runCC()
{
CastleCrash sim = new CastleCrash();
sim.runCastleCrash();
}
My code for the other class:
public void runCastleCrash()
{
System.out.println("Castle Crash is beginning...");
//Other method parts here to be added
}
Hope this is helpful, good luck ! :)
McDowell already answers practically with good examples on how to access variables from event listeners (or anonymous inner classes in general). There is however a more general Sun resource on Event Listeners in Swing that is canonical and a good overview of all the caveats to take into account when writing them.
Somehow you need a reference to your CastleCrash object available to call from your actionListener.
You probably want to subclass JFrame, or whatever is containing your JButton such that it has your both your main method and a CastleCrash property that can then be referenced from your anonymous inner class Actionlistener.
BUT - be careful, you look like you are calling what will be a long running method from within the GUI event thread (where the action listener will called). This is generally a bad idea, you will case your GUI to become unresponsive.
See http://java.sun.com/products/jfc/tsc/articles/threads/threads1.html especially the bit on SwingWorker class for ideas on how to avoid that problem.

Categories

Resources