I'm trying to use and learn the composition's relation between classes respecting the flexibility of OOP programming, a small sample code that explains what I'm trying to do:
Main class:
public class MainFrame extends JFrame {
private Test test;
public static void main(String[] args) {
new MainFrame();
}
public MainFrame() {
initFrame();
}
private void initFrame() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(100,100);
setResizable(false);
setUndecorated(true);
setVisible(true);
setLocationRelativeTo(null);
test.update();
}
public void setNumber(int i) {
System.out.println(i);
}
}
and this class:
public class Test {
MainFrame mainFrame;
public void update(){
mainFrame.setNumber(10);
}
}
if i run this i will get :
Exception in thread "main" java.lang.NullPointerException
how can I change the code so that:
The Test class refers only to the MainFrame instance created in the main, not to create a new instance of the MainFrame inside the Test class
It is possible to avoid the use of static classes and static functions to do this?
The Test class refers only to the MainFrame instance created in the
main, not to create a new instance of the MainFrame inside the Test
class
Sorry, this makes no sense. That's not how Java works, and the runtime is educating you.
This code is wrong from top to bottom.
The instance in the Test class is null because you never initialized it.
The Test class knows nothing about the main method in the MainFrame class, nor should it.
The instance in that main method goes out of scope as soon as the method exits and is garbage collected.
Why would the MainFrame class have a reference to a Test instance inside it? Bad design. It's called a circular dependency. Once you add it, you can never separate the two classes. Sometimes they can't be helped, but I see no reason for it at all here, especially for a class named Test. Test can know about MainFrame, but MainFrame need never know about Test. You've done it wrong.
You don't need to extend JFrame. You aren't adding anything by extending. I'll bet this should be a JPanel that you'll add to a JFrame.
In stead of advising about design, I'll just put a way to achieve it.
public class MainFrame extends JFrame {
private Test test;
public void setTest(Test test) {
this.test = test;
}
}
and
public class Test {
MainFrame mainFrame;
public Test(MainFrame mainFrame) {
this.mainFrame = mainFrame;
this.mainFrame.setTest(this);
}
}
Good luck.
Quick fix for your NullPointerExceptions:
public class Test {
MainFrame mainFrame;
public void update(){
mainFrame.setNumber(10);
}
public void setMainFrame(MainFrame mainFrame) {
this.mainFrame = maninFrame;
}
In initFrame():
test = new Test();
test.setMainFrame(this);
test.update();
Otherwise I agree, the concept you're trying has a lot of misunderstanding. Try to separate the Test and MainFrame logic.
Related
I have two .java files in the same package. I am planning on making the first .java file the underlying code and the second .java file the GUI swing interface.
My problem I encountered was when working on the GUI part of the project, I needed to access several methods from the .java file with the code. My .java file with the code is a like this:
package same;
public class HFSim extends ApplicationTemplate
{
private static class AppFrame extends ApplicationTemplate.AppFrame
{
public myMethodIWanttoUse()
{
//code
}
And in my GUI .java:
package same;
public class GUI extends JFrame
{
public GUI()
{
public void actionPerformed(ActionEvent e)
{
//this is where I want to use the method from above
Is there a way to get that method to be used in the GUI portion? Or is there a better way to approach this problem? thanks in advance.
You have multiple solutions to your problem. The first question you should answer is how these methods you need to call qualify themselves.
Are they utility methods? (They don't require an instance of an object to work on)
Are they attached to a single instance of an object?
Do you need to call methods of a specific object more than just methods?
You can either:
Declare them static and call them, eg HFSim.AppFrame.myMethoIWanttoUse();
Declare a static instance of the object containing them, eg
public class HFSim extends ApplicationTemplate {
public static final AppFrame appFrame = new AppFrame();
...
}
public class GUI extends JFrame {
public GUI() {
public void actionPerformed(ActionEvent e) {
HFSim.appFrame.myMethodIWanttoUse();
}
}
}
Pass the instance of the object to the other one:
public class GUI extends JFrame {
private final HFSim.AppFrame appFrame;
public GUI(HFSim.AppFrame appFrame) { this.appFrame = appFrame; }
public void actionPerformed(ActionEvent e) {
appFrame.myMethodIWanttoUse();
}
}
Make methodIWantToUse() static by replacing
public myMethodIWanttoUse()
with
public static myMethodIWanttoUse()
Secondly, make AppFrame marked as public instead of private.
Then just call you method like this HFSim.AppFrame.myMethodIWantToUse().
Edit:
Alternatively, you don't have to make your method static. Just add this in your GUI code:
HFSim.AppFrame frame = new HFSim.AppFrame();
frame.myMethodIWantToUse();
Still, no matter what, you have to make AppFrame be public.
I have 2 classes in the class Grafics. I would like to put all the grafic stuff, like create frame, buttons, etc., in the inhalt class. I would like to create all the functions with the grafics.
my question:
is it possible to have the Main in the inhalt class and the constructor in the grafics class?
package uebung2;
import javax.swing.JFrame;
public class Grafics extends JFrame {
private static final long serialVersionUID = 1L;
public Grafics(){
setSize(500,500);
setVisible(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
Class with the main method:
package uebung2;
public class Inhalt {
public void main(String[]args){
Grafics fenster = new Grafics();
}
}
Of course it is - main is just the program entry point and it is generally preferable not to mix application glueing (like starting the program, setting up the environment) and the "business logic" of the program.
This is how my code looked in the beginning: https://gist.github.com/anonymous/8270001
Now I removed the ActionListener to a separate class: https://gist.github.com/anonymous/8257038
The program should give me a little UI, but it just keeps running without any UI popup or errors.
Someone told me this:
In your GUI class constructor, you are creating a new nupuVajutus object, but since nupuVajutus extends the GUI class, when you create a nupuVajutus, you are also inherently calling the GUI class constructor by default, thus creating an infinite loop
If this is really the problem, then I have to say I am not that good and could use some help getting this program working with the classes separated.
You have indeed already been given the answer, although what you have is not an infinite loop, but infinite recursion, which will eventually cause a StackOverflowError.
Here's what happens:
new GUI() calls new nupuVajutus(). This creates a new nupuVajutus object by calling its constructor. Because nupuVajutus extends GUI, this means a nupuVajutus object is a GUI object with additional functionality. Therefore, because it is a GUI object, a GUI constructor needs to be called. The nupuVajutus constructor does not explicitly call a super constructor, so it implicitly calls the GUI() (no argument) constructor before executing. In this new call to the GUI() constructor, another new nupuVajutus() call is encountered, and so on, ad infinitum...
It seems to me you need to do some more research around Object Oriented Programming, in particular the topics of sub-classing, inheritance, object instances, and encapsulation. There are plenty of resources available to help you.
After you extracted your ActionListener into a separate file, you should not have changed it to extend GUI. That extends the class (which is like a blueprint) not an instance (which is like a something built using that blueprint) - remember: you can create multiple instances of a class.
Previously, the "nupuVajutus" ActionListener was an inner class, so it had access to all of the enclosing class' fields and methods. Now that it is no longer an inner class, it needs to be passed a reference to the GUI instance so that it can access its methods. Something like this:
public class NupuVajutus implements ActionListener {
private final GUI gui;
public NupuVajutus(GUI gui) {
this.gui = gui;
}
public void actionPerformed(ActionEvent e) {
// The GUI instance can now be accessed through the gui field, for example:
gui.something();
// ...
}
}
And in the GUI() constructor:
NupuVajutus nV = new NupuVajutus(this);
To be honest, though, there is nothing wrong with keeping your ActionListener as an inner class. If you're never going to use that class outside of the GUI class, then it is probably preferable for it to remain as an inner class.
What you are doing it extending the GUI class. This Does Not make then share the Same Fields Say you have a field field in your GUI class
public class GUI {
String field = "Hello";
}
Just because your Listener class extends GUI doesn't mean they will share the exact same field object. I think that's what you think is supposed to occur
public class Listener extends GUI implements ActionListener {
public void actionPerformed(ActionEvent e) {
field = "World";
}
}
The above does nothing the field in GUI. If you were to do this, you would need to access in a static way like line GUI.field = "World";. The above is also what causes in an infinite loop, as you need to instantiate the Listener in the GUI class. This is not really good practice or design.
One option would to use some sort of MVC pattern.
Another option would be to pass the values you need, to a constructor in your Listener class, and instantiate it in your GUI class with those values.
Run this example to see what I'm talking about. I have a MyListener class that I pass a Jlabel to, the same JLabel in the GUI class
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class FieldTest {
private JLabel label = new JLabel(" ");
private JButton button = new JButton("Set Text");
public FieldTest() {
MyListener listener = new MyListener(label);
button.addActionListener(listener);
JFrame frame = new JFrame();
frame.add(label, BorderLayout.CENTER);
frame.add(button, BorderLayout.SOUTH);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new FieldTest();
}
});
}
}
class MyListener implements ActionListener {
JLabel label;
public MyListener(JLabel label) {
this.label = label;
}
#Override
public void actionPerformed(ActionEvent arg0) {
label.setText("Hello, FieldTest!");
}
}
Given two classes, creating an object of each class in one another results in StackOverflow Exception. It is a JAVA project btw.
There are multiple classes in my projects and for using the other classes, I thought i would create objects of the other class and use it.
Say i have class Main and class GUI. I have created object of GUI in MAIN and initialized it. Similarly i have created an object of MAIN in GUI and initialized it.
Now this gives me a Stack Overflow Exception as the the constructor calls are going deep into recursion.
How do i go about it?
One possible solution i can think of is making variables and methods of one class STATIC.
Any other solution? Please suggest.
You should be passing an instance of one of you classes into the constructor of the other class.
public class Main {
private final GUI gui;
Main() {
gui = new GUI(this);
}
}
public class GUI {
private final Main main;
public GUI(Main main) {
this.main = main;
}
}
You could also use setters instead of constructors. I don't like this option as much, because you lose the ability to make your variables final.
public class Main {
private GUI gui;
Main() {
}
public void setGui(GUI gui) {
this.gui = gui;
}
}
public class GUI {
private Main main;
public GUI() {
}
public void setMain(Main main) {
this.main = main;
}
}
public static void main(String[] args) {
Main main = new Main();
GUI gui = new GUI();
main.setGui(gui);
gui.setMain(main);
}
Singleton ? (if it works for your app )
I have this class, let's say Foo. This class is responsible for creating/showing a JFrame, which requires heavy customization.
public class Foo{
private static JFrame frame;
public static void createAndShowFrame(){
//do stuff
}
public static JFrame getFrame(){
return frame;
}
}
And I have this other class, let's say Launcher, that serves as the entry-point of the application. This class is responsible for initiating the construction of the GUI
public class Launcher{
public static void main(String[] args){
SwingUtilities.invokeLater(new Runnable(){
#Override
public void run(){
Foo.createAndShowFrame();
}
});
}
}
The design problem is getFrame(). This method was required because other dialogs require this as their parent frame. Unfortunately, this exposes the frame and seems to void the static-utility method.
I guess my question is, is there a more sound design approach to this? Am I making any sense? I basically wanted to create and show a frame without having to extend it.
See my Rule of Thumb question.
Independently of the question of exposing the JFrame or encapsulating it, why does this class (the methods/fields) have to be static? The object oriented way would be to have them ordinary (instance) methods:
public class FrameCreator {
private JFrame frame;
public void createAndShowFrame(){
//do stuff
}
public JFrame getFrame(){
return frame;
}
}
Then in your launcher class, you would create an object of this class and call it's methods:
public class Launcher{
public static void main(String[] args){
final FrameCreator f = new FrameCreator();
SwingUtilities.invokeLater(new Runnable(){
#Override
public void run(){
f.createAndShowFrame();
}
});
}
}
If you need to access the created frame from somewhere else, pass this code the FrameCreator instance that created the frame.
I don't see the advantage of the use of invokeLater() (EDIT - in this case (since you call it from main() ) . If you have a heavy GUI processing to do, just do it on main thread (because you have to anyway). If most of the work can be done in background, do it in background and publish the results on the main thread. (using invokeLater()). [This part is only my opinion, not based on some docs etc...]
Finally, if you don't want to expose the frame, create a method setChildComponent(JComponent component) that will receive a JComponent and set the frame as its parent internally. This way you would avoid exposing the private JFrame.