Understanding OOP and static in Java - java

Suppose you have class A and class B. Class A is the main class which builds a frame with a GUI. It contains all the GUI variables (such as buttons, labels, strings) along with whatever methods that you'll be using. Class A also creates a class B object:
ClassB name = new ClassB();
Inside class B you will find a for loop. Now, once the for loop is finished looping, I want to call a method located in class A. Whenever I try calling a method located in class A, Eclipse suggests making that method static. I'm trying to avoid making static methods. Is there a way of calling class A's methods from class B without making anything static?
Class A:
public class Game extends JFrame implements ActionListener {
// init variables
private JPanel contentPane;
private JPanel panel_actions;
private JButton btn_strike;
private JProgressBar progBar_loading;
private Load load;
// create the frame
public dsgsd() {
load = new Load();
// frame initializing
setResizable(false);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
setBounds(100, 100, 890, 480);
setTitle("BeyondInfinity - Group Project for CS1100");
getContentPane().setLayout(null);
setVisible(true);
// create a root panel
contentPane = new JPanel();
contentPane.setLayout(null);
contentPane.setBounds(0, 0, 884, 451);
contentPane.setVisible(true);
getContentPane().add(contentPane);
// create actions panel for displaying attack buttons
panel_actions = new JPanel();
panel_actions.setBorder(new EmptyBorder(10, 10, 10, 10));
panel_actions.setBounds(10, 306, 854, 68);
panel_actions.setBackground(new Color(100, 149, 237));
panel_actions.setLayout(new GridLayout(0, 6, 10, 0));
panel_actions.setVisible(true);
contentPane.add(panel_actions);
// create attack button #1
btn_strike = new JButton("Strike");
btn_strike.setFocusable(false);
btn_strike.setVisible(true);
btn_strike.addActionListener(this);
panel_actions.add(btn_strike);
}
// create action listener
public void actionPerformed(ActionEvent evt) {
if (evt.getSource().equals(btn_strike)) {
load.start();
}
}
public void executeTasks() {
//TODO do something
}
// set value for the loading bar
public void setProgBar_loading(int val) {
progBar_loading.setValue(val);
progBar_loading.repaint();
}
}
Class B:
public class Load {
private Timer timer;
private int i;
public void start() {
// reset loading bar
Game.setProgBar_loading(0);
i = 0;
ActionListener listener = new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (i > 100) {
timer.stop();
Game..executeTasks();
} else
Game.setProgBar_loading(i++);
}
};
// timer which triggers the actionlistener every 15ms
timer = new Timer(15, listener);
timer.start();
}
}

You're going to need a reference to an instance of ClassA inside of ClassB to avoid static methods.
First, ClassB will need a field and constructor similar to the following:
private ClassA parent = null;
public ClassB(ClassA parent) {
this.parent = parent;
}
Then, when you instantiate ClassB, you'll pass a reference to the current instance like so: ClassB name = new ClassB(this)
Finally, when you want to use your method inside ClassB (let's assume that method is called doSomething(), you can call it with parent.doSomething()

The "proper" OOP approach to do this would be with an interface.
public interface Loadable {
void reset();
void setProgress(int progress);
void onLoaded();
}
You implement this in your Game class
public class Game extends JFrame implements ActionListener, Loadable {
private JButton load_button;
private JProgressBar progressBar;
public Game() {
// initialize
}
public void executeTasks() {
//TODO do something
}
#Override
public void actionPerformed(ActionEvent e) {
if (e.getSource().equals(load_button)) {
new Loader().load(this);
}
}
#Override
public void onLoaded() {
executeTasks();
}
#Override
public void reset() {
progressBar.setValue(0);
}
#Override
public void setProgress(int progress) {
progressBar.setValue(progress);
}
}
And pass it into the Loader. This way, the Loader doesn't care the you are giving it a Game object. You could have any implementation of Loadable that this Loader can load.
public class Loader {
private Timer timer;
private int i;
public void load(final Loadable l) {
// reset loading bar
l.reset();
i = 0;
ActionListener listener = new ActionListener() {
public void actionPerformed(ActionEvent e) {
l.setProgress(++i);
if (i >= 100) {
timer.stop();
l.onLoaded();
}
}
};
// timer which triggers the actionlistener every 15ms
timer = new Timer(15, listener);
timer.start();
}
}

You can call non-static methods of an object, but you can only call static methods on a class. See this for more information on the difference between the two. To be able to call a method on an object of type ClassA, you'd have to do something like this:
public class ClassA {
public method myNonStaticMethod() { ... }
}
public class ClassB {
private ClassA a;
public ClassB(ClassA a) {
this.a = a; //This makes sure this *object* has a *reference* to an object of *type* ClassA.
}
public void looping() {
//some looping code
this.a.myNonStaticMethod(); //Actually call the nonstatic method
}
}
Note that any class that imports another class will have reference to that class and can call static methods on that class, or instantiate a new object of that class type. If you want a particular object to call a non-static method of another object, it either needs to instantiate the object itself, or it needs a reference to that other object.
In the example above, the constructor of ClassB gets a reference to a specific object of type ClassA, whose reference name is a. This is saved to a private member field, which can be called by any non-static method in an object of type ClassB.

To call any non-static method of A, you need a class instance of A. Do you have one inside class B? If you don't then you can't call a non-static method. A static method can be called without the instance (as long as it is accessible) like this A.method1();

Related

Change value of public variable in Java, used by multiple frames

I have two frames in NetBeans 9.0 as frame1.java, frame2.javaand the main class as main.java.
If I declare a public variable in frame1.java as
public String stringName;
and a function fn() which gives the value of stringName in frame1as say "abcd".
When I write this in frame2,
frame1 fm = new frame1();
String str = frame1.stringName;
System.out.print(str);
I get the output as null. But what I require is "abcd".
What am I doing wrong, and what should it be?
Thanks for help!
Edit:
I have linked frame1 and frame2 such that the GUI from frame1 leads to frame2, and so does the value.
Edit 2
The process goes like this:
GUI of frame1 is visible >> based on user's input, function fn() stores the value, say "abcd" in stringName >> a button click in frame1 leads to frame2>> variable str gets the value from stringName >> System.out.print(str) outputs the value as null .
CODE
frame1:
public class frame1 extends javax.swing.JFrame {
public String stringName;
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt){
stringName = jTextField1.getText(); // gets a not null value
}
}}
frame2:
public class frame2 extends javax.swing.JFrame {
frame1 fm = new frame1();
String str = frame1.stringName;
System.out.print(str); //outputs a null value
}
The point ist that you are crating a new Instance (frame1, fm) in your class frame2. So the value from the string in this new Instance is null. You need a reference to your old Instance which you maybe have initialised in your main method?
Something like that:
String str = myOldInstance.stringName;
But you should create getter an setter and make your var private.
But to help you exactly we need more Code.
in this case the best is Listener pattern.
Create interface of listener, which will inform about change text. In class - target of this information - create instance of this listener and return that. In class - source of information - set listener and put on field.
When you want inform of change text, you fire method of listener, and on seconde frame will execute implementation of method.
Below example - I fire on button click.
Any way, field should be private, and add getter and setter. Public fields are bad.
Main class
public class App {
public static void main(String[] args) {
Frame1 f1=new Frame1();
Frame2 f2=new Frame2();
TextListener textListener = f2.getListener();
f1.setListener(textListener);
}
}
Listener
public interface TextListener {
public void onTextPropagate(String text);
}
Frame classes
public class Frame1 extends JFrame{
private TextListener listener;
JButton button;
public Frame1() {
super("Frame1");
setBounds(200, 200, 400, 600);
button=new JButton("Action");
button.setBounds(100, 200, 200, 100);
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if(listener!=null) {
String text = UUID.randomUUID().toString();
System.out.println("On Frame1:\t"+text);
listener.onTextPropagate(text);
}
}
});
this.add(button);
setVisible(true);
}
public void setListener(TextListener listener) {
this.listener=listener;
}
}
public class Frame2 extends JFrame{
public Frame2() {
super("Frame2");
setBounds(100, 100, 200, 400);
setVisible(true);
}
public TextListener getListener() {
return new TextListener() {
#Override
public void onTextPropagate(String text) {
reactOnChangeText(text);
}
};
}
private void reactOnChangeText(String text) {
System.out.println("On Frame2:\t"+text);
}
}

Avoid infinite loop when Java Swing Action Listener class extends another class

I've attached an ActionListener to a JButton in Swing.
The main class:
class MainClass {
String foo;
JButton button = new JButton("cool button");
public MainClass(String foo) {
this.foo = foo;
...
JFrame setup here
...
button.addActionListener(new MyBtnListener(frame));
System.out.println(getFoo());
}
public String getFoo() {
return this.foo;
}
}
The class that implements the ActionListener methods:
class MyBtnListener extends MainClass implements ActionListener {
private JFrame target;
public MyBtnListener(JFrame target) {
this.target = target;
}
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("button clicked");
//target.dispose();
}
}
When compiled, the code results in something like this:
foofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoo (an infinite loop). I need the ActionListener class to be able to access methods in MainClass, while at the same time not repeatedly calling the constructor. How can I achieve this?
The answer to your question is pretty straight forward: what you search for is called a „reference“.
Just add a private field of the type MainClass to your listener and initialise it in the constructor:
private final JFrame frame;
private final MainClass reference;
public MyBtnListener(final JFrame frame, final MainClass reference)
{
this.frame = frame;
this.reference = reference;
}
Then you can just invoke the getFoo method like this:
final String foo = reference.getFoo();
It is also very important that the MyBtnListener class doesn't extend the MainClass class.
By having MyBtnListener extend MainClass and MainClass having creating a MyBtnListener in its constructor you have created a loop that lasts as long as there is space on the call stack.
Move the button.addActionListener(new MyBtnListener(frame)); to the constructor of MyBtnListener as button.addActionListener(this); or don't have MyBtnListener extend MainClass, just have it implement ActionListener.
class MainClass {
String foo;
JButton button = new JButton("cool button");
public MainClass(String foo) {
this.foo = foo;
...
JFrame setup here
...
button.addActionListener(new MyBtnListener());
System.out.println(getFoo());
}
public String getFoo() {
return this.foo;
}
}
class MyBtnListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("button clicked");
}
}

How to repaint JPanel from outside its parent JFrame?

I can add/remove elements to/from a panel and repaint it when the method used to fill the panel is called by one of its parent JFrame events, but I can not repaint it by events from other classes even if their sources have been added to it, or that is how I understand the problem for now.
I want to understand what is going on here, Thank you.
Main Class
public class Principal extends JFrame implements ActionListener{
private static Principal instPrincipal = null;
private SubClass subClassInst =new SubClass();
public JPanel panelPrincipal;
public static Principal getInstance() {
if (instPrincipal != null)
return instPrincipal ;
else {
instPrincipal = new Principal ();
return instPrincipal ;
}
}
public void actionPerformed(ActionEvent event) {
Object source = event.getSource();
try {
if(source == btnSub)
{
subClassInst.fillPanelPrincipal();
}
}
catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
Sub Classes Example
public class SubClass implements ActionListener {
private JPanel tempPanel;
private JButton btnSave;
private Principal instPrincipal;
public void fillPanelPrincipal() {
instPrincipal = Principal.getInstance();
instPrincipal.panelPrincipal.removeAll();
//Start adding elements..
tempPanel = new JPanel();
instPrincipal.panelPrincipal.add(tempPanel);
btnSave = new JButton("Save");
btnSave.addActionListener(this);
tempPanel.add(btnSave);
//End.
instPrincipal.panelPrincipal.repaint();
}
public void actionPerformed(ActionEvent event) {
instPrincipal = Principal.getInstance();
Object source = event.getSource();
if (source == btnSave) {
// modify local data, Database .. ; //work but need to be repainted on panelPrincipal
instPrincipal.panelPrincipal.repaint();//does not work
}
}
}
Update
To clarify the problem more, I have one single JPanel on a JFrame and there are different classes to fill it for multiple functionalities, I call their methods using JMenuItems on the main frame, these Classes implement ActionListener, passing the panel didn't work, and also the method I am trying here.
I thought about changing the design to use CardLayout, but it was very difficult.
You are calling Principal as a static reference, so how is it supposed to know what frame to repaint? You should pass the instance of the JFrame through the constructor of the subclass. Like so:
private SubClass subClassInst = new SubClass(this);
And create the constructor like this
private JFrame parent;
public SubClass(JFrame parent) { this.parent = parent; }
You can then use it like so
this.parent.repaint();

Java - Reusable button action handler concepts question

First off - sorry for the wall of code but it's not too horrendous, just a framework for what I'm trying to explain. It runs without errors.
The goal
I'm making a reuseable button class for my GUI and each button object needs to have a different handler when it's clicked. I want to to assign a ClickHandler object to each new button. Then, the button would call init() on the handler, and be on its way. Unfortunately, there's a typing problem, since each handler class would have a different name.
Current progress
Right now, the handler is typed as HandlerA, but I'd like to have it handle any name, like "SettingsHandler" or "GoToTheWahWah" etc.
I've tried messing about with generic types, but since I'm new to this, and from a webdev background, there seems to be a conceptual hurdle I keep knocking over. Is this the right way to approach the problem?
The code
ReuseableButton.java is a reuseable class, the only thing that changes is the action when it's clicked:
package gui;
import java.awt.event.*;
import java.awt.*;
import javax.swing.*;
public class ReuseableButton extends JButton implements ActionListener {
private static final long serialVersionUID = 1L;
// I want a generic type here, not just HandlerA!
private HandlerA ClickHandler;
// Assemble generic button
public ReuseableButton(Container c, String s) {
super(s);
addActionListener(this);
c.add(this);
}
// Once again, generic type, not just HandlerA!
public void SetClickHandler(HandlerA ch) {
ClickHandler = ch;
}
// Call init() from whatever class has been defined as click handler.
public void actionPerformed(ActionEvent e) {
ClickHandler.init();
}
}
Controller.java fires the frame and assembles buttons as needed (right now, only one button).
package gui;
import javax.swing.*;
import java.awt.*;
public class Controller extends JFrame {
private static final long serialVersionUID = 1L;
public Controller() {
JFrame frame = new JFrame("Handler Test GUI");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container pane = frame.getContentPane();
pane.setLayout(new FlowLayout());
ReuseableButton b = new ReuseableButton(pane,"Reuseable Button A");
// THE QUESTION IS HERE: Pass a generic object?
b.SetClickHandler(new HandlerA());
frame.pack();
frame.setSize(200,200);
frame.setVisible(true);
}
public static void main(String args[]) {
new Controller();
}
}
HandlerA.java is a sample of a random handler for the button click. Later, there could be HandlerB, HandlerC, etc.
package gui;
// A random handler
public class HandlerA {
public void init() {
System.out.println("Button clicked.");
}
}
Thanks very much in advance!
All of you handlers should implement an interface like Clickable or something. That way the interface specifies the existence of the init function:
public interface Clickable
{
public void init();
}
Making the HandlerA definition:
public class HandlerA implements Clickable {
public void init() {
System.out.println("Button clicked.");
}
}
I recommend to work with inheritence in this case:
public abstract class AbstractHandler {
public abstract void init();
}
Then:
public class ConcreteHandlerA extends AbstractHandler {
#Override
public void init() {
// do stuff...
}
}
Controller
public class ReuseableButton extends JButton implements ActionListener {
// I want a generic type here, not just HandlerA!
private AbstractHandler ClickHandler;
public Controller() {
//...
ReuseableButton b = new ReuseableButton(pane,"Reuseable Button A");
AbstractHandler handlerA = new ConcreteHandlerA();
b.SetClickHandler(handlerA);
// ...
}
}
Not sure if this is what you're looking for...
BTW: You can define the AbstractHandler as an interface as well, but you may want to implement some common logic here as well - shared across handlers.
You should use an interface for the handler.
public interface ClickHandler() {
void init();
}
ReuseableButton b = new ReuseableButton(pane,"Reuseable Button A");
b.SetClickHandler(object which implements the ClickHandler interface);
This is the same concept as the normal JButton. There you have the ActionListener interface and the actionPerformed method in it.
P.S. If I don't understand your question, please correct me.

Access an object method created in main class from another class

Is it possible to access an object created in main class? or there is a way of get the owner/reference class?
For example, in the code below, how can I call the method setMenu, that is on the myMainClass, from one of the Menu class objects (firstMenu, secondMenu)
I can make the Menu objects static but doesn't seems like the right approach...
Main Class
public class myMainClass extends JFrame {
JPanel container;
Menu firstMenu;
Menu secondMenu;
myMainClass() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(400, 400);
firstMenu = new Menu();
secondMenu = new Menu();
container = new JPanel(new CardLayout());
container.add(firstMenu, "firstMenu");
container.add(secondMenu, "secondMenu");
add(container);
}
public void setMenu(String s) {
CardLayout cl = (CardLayout) (container.getLayout());
cl.show(container, s);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
myMainClass myMainClassObject = new myMainClass();
myMainClassObject.setVisible(true);
}
});
}
}
Menu Class
public class Menu extends JFrame {
Menu() {
//How can I call myMainClass setMenu method from here?
//myMainClass.setMenu("secondMenu");
//myMainClassObject.setMenu("secondMenu");
}
}
Thanks
You've answered your own question. Make the setMenu method static.
public void setMenu(String s) {
CardLayout cl = (CardLayout) (container.getLayout());
cl.show(container, s);
}
And invoke it using the class name MyMainClass.setMenu in the Menu class.
Otherwise, you'll have to pass the instance of MyMainClass to the Menu class by overloading its constructor.
firstMenu = new Menu(this);
secondMenu = new Menu(this);
And in the other class,
public class Menu extends JFrame {
Menu() {
//How can I call myMainClass setMenu method from here?
//myMainClass.setMenu("secondMenu");
//myMainClassObject.setMenu("secondMenu");
}
Menu(MyMainClass main) {
main.setMenu("secondMenu");
}
}
setMenu() is an instance method if you want to call it you would need to instantiate myMainClass, which btw should be called MyMainClass, so you need to do:
MyMainClass mainClass = new MyMainClass();
mainClass.setMenu("secondMenu");
If you want the method to be accessed via the class then you would need to create the method static, so it is a class method and not an instance method, so you can call it like:
MyMainClass.setMenu("secondMenu");

Categories