Anonymous inner class - How javac work with lambda expression? [duplicate] - java

This question already has answers here:
How will Java lambda functions be compiled?
(2 answers)
Closed 5 years ago.
In the below working code,
package com.ca.naive;
import java.awt.BorderLayout;
import java.awt.Button;
import java.awt.FlowLayout;
import java.awt.Frame;
import java.awt.GridLayout;
import java.awt.List;
import java.awt.Panel;
import java.awt.TextField;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
public class TodoGUI {
public static void main(String[] args) {
List list = new List();
TextField itemField = new TextField();
Button addButton = new Button("Add");
Button removeButton = new Button("Remove");
addButton.addActionListener(e -> list.add(itemField.getText()));
removeButton.addActionListener( e -> list.remove(list.getSelectedIndex()));
Panel buttons = new Panel(new GridLayout(1,0,3,3));
buttons.add(addButton);
buttons.add(removeButton);
Panel bottomPanel = new Panel(new FlowLayout(FlowLayout.RIGHT));
bottomPanel.add(buttons);
Panel centerPanel = new Panel(new BorderLayout());
centerPanel.add(BorderLayout.NORTH, itemField);
centerPanel.add(BorderLayout.SOUTH, buttons);
Frame frame = new Frame();
frame.setLayout( new BorderLayout() );
frame.add(BorderLayout.WEST, list);
frame.add(BorderLayout.CENTER, centerPanel);
frame.pack();
frame.addWindowListener( new WindowAdapter() {
#Override
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
frame.setVisible(true);
}
}
Does javac replace addButton.addActionListener(e -> list.add(itemField.getText())); syntax with
addButton.addActionListener(new java.awt.Event.ActionListener() {
public void actionPerformed(java.awt.Event.ActionEvent e) {
list.add(itemField.getText());
}
});
?

Does javac replace addButton.addActionListener(e -> list.add(itemField.getText())); syntax with
addButton.addActionListener(new java.awt.Event.ActionListener() {
public void actionPerformed(java.awt.Event.ActionEvent e) {
list.add(itemField.getText());
}
});
?
No, this is not what the compiler does. In your example, an ActionListener implementation will be generated and instantiated, but that does not happen at compile time; it happens at runtime. The compiler does two things. First, it moves the body of your lambda into a hidden method that looks something like this:
void lambda$1(java.util.List list, java.awt.TextField itemField) {
list.add(itemField.getText());
}
Second, at the point where your lambda is declared, it emits a call to a bootstrap method. The bootstrap method is a special factory method that knows how to generate an implementation of a functional interface. It needs to be given some basic information, most notably: the type of the functional interface (already known to be ActionListener); the types of any captured variables (in your case, list and itemField); and which method contains the logic for the implementation (the generated lambda$1 method).
When the bootstrap call gets hit at runtime, it will generate an ActionListener implementation. The next time you end up on this code path, you won't have to call the bootsrap method. Instead, the bootstrap call is replaced such that you end up with something equivalent to:
addButton.addActionListener(TodoGUI$Lambda$1.getInstance(list, itemField));
Where TodoGUI$Lambda$1 is a class that looks something like this:
static class TodoGUI$Lambda$1 implements java.awt.Event.ActionListener {
private final java.util.List list;
private final java.awt.TextField itemField;
TodoGUI$Lambda$1(java.util.List list, java.awt.TextField itemField) {
this.list = list;
this.itemField = itemField;
}
#Override
public void actionPerformed(java.awt.Event.ActionEvent e) {
TodoGUI.lambda$1(list, itemField);
}
static java.awt.Event.ActionListener getInstance(
java.util.List list,
java.awt.TextField itemField) {
return new TodoGUI$Lambda$1(list, itemField);
}
}
Now, with all that in mind, the compiler does not need you to import the ActionListener type. That type does not need to be in the lexical scope at all. The compiler will look and see that you are calling a method called addActionListener on an java.awt.Button instance. It will see that you are passing a single argument, which is a lambda expression. In this case, there are no overloads, so it knows that addActionListener expects you to pass an ActionListener. It sees that ActionListener is a single-method interface, meaning it can be bound to a lambda. It attempts to infer your argument types and return type such that they are compatible with what is expected for an ActionListener: a single ActionEvent argument, and a void return type. Your lambda is compatible, so the call is bound, and the steps above are performed.

Another "non-lambda" example of the effect you described is a call like
System.out.println("hello");
You don't need to import java.io.PrintStream in order to use the println method that comes with out being an instance of this class.
BTW: You can program a class without the use of any import statements. In that case you always have to use the full classname including all packages if you want to use it:
java.io.PrintStream outStream = System.out;
outStream.println("Hello");
An import just saves you the repeated specification of the package each time you use a class.

Related

Nothing happens when I try to apply an ActionListener to a JButton to change the content of the JLabel

I need a way to create an ActionListener that when a JButton is pressed, it updates the content of 7 different JLabels to display the information in the form of text.
The data is retrieved from methods called from an external JAR file. The methods return ArrayList. I attempted to convert the ArrayList into a String, and tried to change the JLabel content with setText().
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import api.anAPI.THEAPINAME;
public class Controller implements ActionListener {
private MainGUI maingui;
private SubPanel subpanel;
private static THEAPINAME anAPI =new THEAPINAME("XyP0D75oRCGrLE78","x47ka5jmOGaJ2zvw");
static ArrayList<String> nameList =new ArrayList<String>();
private String names;
public Controller(MainGUI maingui,SubPanel subpanel){
this.maingui = maingui;
this.SubPanel = subpanel;
MainGUI.getSearchBtn().addActionListener(this);
nameList.addAll(anAPI.getNames());
for (String s: nameList){
names+= s+"\t";
}
}
public void actionPerformed(ActionEvent e) {
SubPanel.label1.setText(names);
//6 more Labels.
}
}
An additional, because I have 7 JLabels, would I need to do 7 getLabel methods? Or is there a way to get them all with just 1 method.
I am not entirely sure what I am doing incorrectly, it could be that the getMethods I used returned the wrong widget in question as the code for the GUI was not done by me but by a teammate and he had done a really poor job of making it clear for us.
UPDATE:
Fixed up the GUI to make it clearer, so I think that is no longer the problem. Now I think the problem might be that I did not convert the contents of the ArrayList into a String in the way I thought.
The desired function of the code is when the JButton is clicked on, the JLabels in question are all updated to their relevant data.
addController method
public void addController(Controller controller){
control = controller;
jb1.addActionListener(control);
}
You didn't really describe what the problem is of your current code.
You can add a method say getLabels() in SubPanel class to return all of its labels, or you can add a method setLabelText(String text) to set text for all of its labels by extending or directly modifying SubPanel class.
UPDATE
You have several very confusing parts in your code.
In your constructor, it should be this.subpanel = subpanel and then it should be maingui.getSearchBtn().addActionListener(this), also in method actionPerformed it should be subpanel.label1.setText(names). These might not be your problems though since you didn't say it's the code you're actually running.
Looks like that you haven't created any instance of class Controller thus the code in it never gets executed.
You need to have some code outside of you Controller class like this:
MainGUI maingui;
SubPanel subpanel;
// they're somehow initialized
Controller controller = new Controller(maingui, subpanel);

FocusListener on JTextField not working

I've created an application that uses FocusListener to make sure a text fieid's value is always positive. When the user inputs negative value and then click the "tab" key to move focus away from the text field, the value will be multiplied by -1 so that the resulted value is positive. However, when I ran the application, the text field didn't change. I am not sure what I did wrong, and will appreciate any help.
Here is my code:
import java.awt.event.*;
import javax.swing.*;
import java.awt.*;
public class AlwaysPositive extends JFrame implements FocusListener {
JTextField posField = new JTextField("30",5);
public AlwaysPositive() {
super("AlwaysPositive");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel pane = new JPanel();
JTextField posField = new JTextField("30",5);
JButton ok= new JButton("ok");
posField.addFocusListener(this);
pane.add(posField);
pane.add(ok);
add(pane);
setVisible(true);
}
public void focusLost(FocusEvent event) {
try {
float pos = Float.parseFloat(posField.getText());
if (pos < 0)
pos = pos*-1;
posField.setText("" + pos);
} catch (NumberFormatException nfe) {
posField.setText("0");
}
}
public void focusGained(FocusEvent event) {
}
public static void main(String[] arguments) {
AlwaysPositive ap = new AlwaysPositive();
}
}
The main problem is you are shadowing your variables
You declare
JTextField posField = new JTextField("30",5);
As an instance variable, but in your constructor, you redeclare it again...
public AlwaysPositive() {
//...
JTextField posField = new JTextField("30",5);
posField.addFocusListener(this);
//...
}
Add attach the focus listener to it, but in the focusLost method, you are referring to the instance variable, which isn't the one that is actually on the screen
Start by changing the declaration within the constructor
public AlwaysPositive() {
//...
posField = new JTextField("30",5);
posField.addFocusListener(this);
//...
}
However, there are better solutions to use then FocusListener.
For example, you could use an InputVerifier that will allow you to verify the value of the field and make decisions about whether focus should be moved or not.
Take a look at How to Use the Focus Subsystem and Validating Input in particular
You could also use a DocumentFilter to restrict what the user can actually enter, filtering the input as the user types it. Take a look at Text Component Features and Implementing a Document Filter in particular.
You can also take a look at these examples for more ideas
When you create object of same name inside a method, the listener is set to the method object and not to the Class object.

Multiple Jpanel with buttons in one Jframe on MVC, how do I get the actionlistener in my controler ?

this is a homework btw,
I am asked to make a jframe containing multiple jpanels which have buttons and action listeners attached to them. I have to use the MVC model to do it but, since my buttons/actions are in jpanels instead of the jframe, i do not know how to recover them. I wont put down all of my code but, just what is needed to see what I try to do. I want to get the button "ajouter" from panel 3 first to do whatever action:
So this is pannel 3
import java.awt.FlowLayout;
import javax.swing.JButton;
import javax.swing.JPanel;
import ca.uqam.inf2120.tp2.modele.GestionAbsenceEmployes;
public class RechercherAbsenceP3 extends JPanel {
private GestionAbsenceEmployes aControleur;
private JButton ajouter, modifier, supprimer, afficher, fermer;
FlowLayout gestionnaireComposant;
RechercherAbsenceP3() {
try {
jbInitP3();
} catch (Exception e) {
e.printStackTrace();
}
ajouter.addActionListener(aControleur);
modifier.addActionListener(aControleur);
supprimer.addActionListener(aControleur);
afficher.addActionListener(aControleur);
fermer.addActionListener(aControleur);
}
private void jbInitP3() throws Exception {
gestionnaireComposant = new FlowLayout(FlowLayout.RIGHT);
this.setLayout(gestionnaireComposant);
ajouter = new JButton("Ajouter");
modifier = new JButton("Modifier");
modifier.setEnabled(false);
supprimer = new JButton("Supprimer");
supprimer.setEnabled(false);
afficher = new JButton("Afficher");
afficher.setEnabled(false);
fermer = new JButton("Fermer");
this.add(ajouter);
this.add(modifier);
this.add(supprimer);
this.add(afficher);
this.add(fermer);
}
public JButton getAjouter() {
return ajouter;
}
}
This is the window
package ca.uqam.inf2120.tp2.interfacegraphique;
import java.awt.BorderLayout;
import ca.uqam.inf2120.tp2.interfacegraphique.RechercherAbsenceP3;
import javax.swing.JFrame;
import javax.swing.JPanel;
import ca.uqam.inf2120.tp2.modele.GestionAbsenceEmployes;
public class CreerRechercherAbsence extends JFrame {
private GestionAbsenceEmployes aControleur;
private JPanel absenceP1, absenceP2, absenceP3;
private BorderLayout gestionnaireComposant;
public CreerRechercherAbsence() {
super("Gestionnaire des employés absents");
try {
jbInit();
} catch (Exception ex) {
ex.printStackTrace();
}
aControleur = new GestionAbsenceEmployes(this);
}
void jbInit() throws Exception {
gestionnaireComposant = new BorderLayout(5, 5);
this.getContentPane().setLayout(gestionnaireComposant);
absenceP1 = new RechercherAbsenceP1();
absenceP2 = new RechercherAbsenceP2();
absenceP3 = new RechercherAbsenceP3();
this.getContentPane().add(absenceP1, BorderLayout.NORTH);
this.getContentPane().add(absenceP2, BorderLayout.CENTER);
this.getContentPane().add(absenceP3, BorderLayout.SOUTH);
}
}
now the not finished controler:
package ca.uqam.inf2120.tp2.modele;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import ca.uqam.inf2120.tp1.partie1.adt.impl.ListeAdtArrayListImpl;
import ca.uqam.inf2120.tp2.interfacegraphique.CreerRechercherAbsence;
public class GestionAbsenceEmployes implements ActionListener{
private AbsenceEmploye modele;
private CreerRechercherAbsence vue;
public GestionAbsenceEmployes(CreerRechercherAbsence uneVue) {
this.modele = new AbsenceEmploye();
vue = uneVue;
}
public AbsenceEmploye getModele() {
return modele;
}
#Override
public void actionPerformed(ActionEvent event) {
Object source = event.getSource();
if(source == vue.getAjouter()) {
}
}
}
When I add the vue.getAjouter() it does not know what it is !!!
What do I do/get wrong ?
The problem is you are calling getAjouter() on CreerRechercherAbsence JFrame instance in your ActionListener where as you'd want to be calling getAjouter() on RechercherAbsenceP3 JPanel instance.
My solution:
Convert your ActionListener class GestionAbsenceEmployes to accept RechercherAbsenceP3 as the parameter so we can call getAjouter() on its instance like so:
class GestionAbsenceEmployes implements ActionListener {
private AbsenceEmploye modele;
private RechercherAbsenceP3 vue;
public GestionAbsenceEmployes(RechercherAbsenceP3 uneVue) {
this.modele = new AbsenceEmploye();
vue = uneVue;
}
public AbsenceEmploye getModele() {
return modele;
}
#Override
public void actionPerformed(ActionEvent event) {
Object source = event.getSource();
if (source == vue.getAjouter()) {
}
}
}
you would than do:
aControleur = new GestionAbsenceEmployes(absenceP3);
but in order for the above statement to function you must change this:
private JPanel absenceP1, absenceP2,absenceP3;
in CreerRechercherAbsence class to this:
private JPanel absenceP1, absenceP2;
private RechercherAbsenceP3 absenceP3;
because you extend JPanel to add functionality hence the RechercherAbsenceP3 but by declaring it as JPanel you down cast it, thus it does not have access to the methods of extended JPanel RechercherAbsenceP3 and only those of default JPanel.
Some other suggestions:
Do not extend JFrame class unnecessarily
No need for getContentPane.add(..) as add(..) has been diverted to contentPane
Be sure to create and manipulate Swing components on Event Dispatch Thread
Not sure whether the following approach will be considered MVC, or whether it will result in good marks on your assignment.
My "problem" with your current approach is that the reusability is limited to the model, and that it looks difficult to write a decent test case for this code, unless you are prepared to write tests which include the whole view.
When I need to write a Swing application, it seems that I only end up with 2 classes: a model class defining the data and the operations available on that data, and the view class. The view class functions both as view as well as controller. When I have a button as in your example, I would attach an ActionListener to it (or use an Action) which just retrieves the necessary information from the view without any logic. It passes all that information directly to the model side where all the logic is located.
The two main benefits I see in this approach:
I can re-design my view without any problems. If I decide to remove a JButton and provide the user with another mechanism for that same operation, all my changes are limited to the view. I have no dependency on UI elements except in my view class. I see all the "information gathering and passing it to the model" directly in my view class, and due to the implementation of that view this will not affect other classes. Compare that with your code where you have a source == vue.getAjouter() check in a class outside your view.
I can test the model and all its logic without needing my actual view. So I can skip the whole "firing up a Swing UI" in a unit test and still test all my logic. If I want to test the UI (for example to test whether a certain button is disabled when a field is left blank) I can test this separately in an integration test (as having a UI tends to slow down your tests).
What I found a very interesting article in this regard is The humble dialog box
Here is how I would do it. Make GestionAbsenceEmployes a non-static inner class of CreerRechercherAbsence
public class CreerRechercherAbsence extends JFrame {
private GestionAbsenceEmployes aControleur;
private JPanel absenceP1, absenceP2;
private RechercherAbsenceP3 absenceP3;
// code omitted
public CreerRechercherAbsence() {
super("Gestionnaire des employés absents");
try {
jbInit();
} catch (Exception ex) {
ex.printStackTrace();
}
aControleur = new GestionAbsenceEmployes();
}
// code omitted
class GestionAbsenceEmployes implements ActionListener{
private AbsenceEmploye modele;
public GestionAbsenceEmployes() {
this.modele = new AbsenceEmploye();
}
public AbsenceEmploye getModele() {
return modele;
}
#Override
public void actionPerformed(ActionEvent event) {
Object source = event.getSource();
if(source == absenceP3.getAjouter()) {
}
}
}
No need to pass this to the constructor and the controller does not need a reference to vue. You get all that for free by making this an inner class. Your controller can access all the member variables of the view. So you can now access the absenseP3 panel with the getAjouter() method.
See http://docs.oracle.com/javase/tutorial/java/javaOO/nested.html for more information on when it makes sense to use inner classes.

variable can not be resolved

I'm working through the book Beginning BlackBerry 7 Development and really putting my little or non existent oop/java skills to the test- eclipse is throwing up an error from the code in the book stating that loginhandler hasn't been declared- and yes its correct- it hasn't been declared but it also hasn't been declared in the book.
What has been done is an inner class called logincommandhandler (at the bottom of my code) which can be seen in an excerpt here- http://my.safaribooksonline.com/book/-/9781430230151/chapter-4-user-interface-basics/67 - this is what its meant to be calling (i assume) but my limited oop skills i do not know what loginHandler should be- should it be defined somewhere with a type?
(there is no erata for the book)
package com.beginningblackberry.uifun;
import net.rim.device.api.system.Bitmap;
import net.rim.device.api.ui.Field;
import net.rim.device.api.ui.UiApplication;
import net.rim.device.api.ui.component.BitmapField;
import net.rim.device.api.ui.container.MainScreen;
import net.rim.device.api.ui.container.HorizontalFieldManager;
import net.rim.device.api.ui.component.Dialog;
import net.rim.device.api.ui.component.EditField;
import net.rim.device.api.ui.component.PasswordEditField;
import net.rim.device.api.ui.component.SeparatorField;
import net.rim.device.api.ui.component.LabelField;
import net.rim.device.api.ui.component.CheckboxField;
import net.rim.device.api.ui.component.ObjectChoiceField;
import net.rim.device.api.ui.component.ButtonField;
import net.rim.device.api.ui.FieldChangeListener;
import net.rim.device.api.ui.component.Menu;
import net.rim.device.api.ui.MenuItem;
import net.rim.device.api.util.StringProvider;
import net.rim.device.api.command.Command;
import net.rim.device.api.command.CommandHandler;
import net.rim.device.api.command.ReadOnlyCommandMetadata;
public class UiFunMainScreen extends MainScreen implements FieldChangeListener {
BitmapField img;
EditField usernameField;
PasswordEditField passwordField;
ObjectChoiceField domainField;
CheckboxField rememberCheckBox;
ButtonField clearButton, loginButton;
public UiFunMainScreen(){
Bitmap logoBitmap = Bitmap.getBitmapResource("img/upd8rLOGO.png");
img = new BitmapField(logoBitmap, Field.FIELD_HCENTER);
add(img);
add(new SeparatorField());
add(new LabelField("Please Enter Your Credentials:"));
usernameField = new EditField("Username:","");
passwordField = new PasswordEditField("Password:","");
domainField = new ObjectChoiceField("Domain",new String[] {"Home","Work"});
rememberCheckBox = new CheckboxField("Remember password",false);
add(usernameField);add(passwordField);
add(domainField);
add(rememberCheckBox);
add(new SeparatorField());
clearButton = new ButtonField("Clear",ButtonField.CONSUME_CLICK);
loginButton = new ButtonField("Login",ButtonField.CONSUME_CLICK);
HorizontalFieldManager buttonManager = new HorizontalFieldManager(Field.FIELD_RIGHT);
buttonManager.add(clearButton);
buttonManager.add(loginButton);
add(buttonManager);
clearButton.setChangeListener(this);
//loginButton.setChangeListener(this);
loginButton.setCommand(new Command(LoginHandler));
}
//routing
public void fieldChanged(Field field, int context){
if(field == clearButton){
clearTextFields();
}else if(field == loginButton){
login();
}
}
private void login(){
if(usernameField.getTextLength()== 0 || passwordField.getTextLength() == 0){
Dialog.alert("You must enter a username and password");
}
else
{
String username = usernameField.getText();
String selectedDomain = (String) domainField.getChoice(domainField.getSelectedIndex());
LoginSuccessScreen loginSuccessScreen = new LoginSuccessScreen(username, selectedDomain);
UiApplication.getUiApplication().pushScreen(loginSuccessScreen);
}
}
public void clearTextFields ()
{
usernameField.setText("");
passwordField.setText("");
}
protected void makeMenu(Menu menu, int instance){
super.makeMenu(menu, instance);
/*
menu.add(new MenuItem(new StringProvider("Login"),20,10){
public void run(){
login();
}
});
*/
//login menu item
MenuItem loginMenu = new MenuItem(new StringProvider("Login"),20,10);
loginMenu.setCommand(new Command(LoginHandler));
menu.add(loginMenu);
//clear text menu item
menu.add(new MenuItem(new StringProvider("Clear"),20,10){
public void run(){
clearTextFields();
}
});
}
class LoginCommandHandler extends CommandHandler
{
public void execute(ReadOnlyCommandMetadata metadata, Object context){
login();
}
}
}
and the error-
LoginHandler cannot be resolved to a variable UiFunMainScreen.java /UiFun/src/com/beginningblackberry/uifun line 69 Java Problem
any blackberry/java wizards shed some light on where i am going wrong?
Update
No one really answered the question bang on- to call the new inner class i called this instead
MenuItem loginMenu = new MenuItem(new StringProvider("Login"),20,10);
loginMenu.setCommand(new Command(new LoginCommandHandler()));
menu.add(loginMenu);
update 2 second answer
Declaring loginHandler as a class variable also works -
LoginCommandHandler loginHandler = new LoginCommandHandler();
My guess would be that it should be an instance of LoginCommandHandler (the class declared at the end). It's a guess, but an educated one: Command's constructor expects a CommandHandler instance, and LoginCommandHandler extends CommandHandler, so...
The easy change is to change all the places that look like this:
loginButton.setCommand(new Command(LoginHandler));
to:
loginButton.setCommand(new Command(new LoginCommandHandler()));
E.g., we're calling the LoginCommandHandler constructor and passing the resulting object into new Command().
Or if for some reason (I haven't really read the code) you need a reference to the handler, declare and instantiate it:
LoginCommandHandler loginHandler = new LoginCommandHandler();
...and then use it
loginButton.setCommand(new Command(loginHandler));
(Note that there are multiple places where they've made this mistake in the that quoted code.)
Your code is missing this line
LoginCommandHandler loginHandler = new LoginCommandHandler();
Moreover you can get code for the book here.
The problem is in this line
loginButton.setCommand(new Command(LoginHandler));
You proabably need a instance of LoginHandler (new LoginHandler()) to pass to new Command method.
As the error indicates, the error is in line 69, which I assume is this:
loginButton.setCommand(new Command(LoginHandler));
Assuming LoginHandler is the name of a class or interface, this line is invalid.
It should be something like new Command(LoginHandler.class), new Command(new LoginHandler()) or maybe new Command("LoginHandler"), since the parameter requires you to pass an object (the type object for a class would be <classname>.class).
If it should be a variable, then it simply doesn't exist (btw. Java convention is that variable names start with a lower case letter) and you have to create it.
... it hasn't been declared but it also hasn't been declared in the book. ... (there is no erata for the book)
This doesn't mean the code in the book is 100% correct and compilable.

updating Swing JTextField

I can't .setText(...) for a JTextField outside of the class that creates the gui. I'm very confused and I feel like there is something basic I am missing. I need some help here.
Here is what I am doing:
In a class (called MainClass) I create an instance of a class that creates my gui
TestText gui = new TestText();
with a constructor that sets the default settings (a JTextField and a button with a listener). Then I call the a setter that I wrote, where I pass it a string that is to set the text of the JTextField:
gui.setText("new");
But "new" doesn't show up on the gui.
I know my setter works from within the class because if I make a call to the setter from the button that I created in gui then the changes show up on the gui.
The part that really confuses me is this: If I call my getter just before my setter, then it returns the old value. Then if I call the getter again after I call the setter then it returns the new value, while the gui continues to show the old value. I thought that maybe it just isn't repainting the gui so I tried all kinds of permutations of .invalidate(), .validate(), .update() and .repaint(), all from the MainClass and from inside the setter. But none did anything.
Is it possible that I somehow have 2 different instances of the gui and I'm only editing one of them?
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class TestText {
private JTextField textField;
private JButton button;
private JPanel frame;
JFrame jFrame;
public void setText(String text) {
textField.setText(text);
}
public String getText() {
return textField.getText();
}
public TestText() {
this.textField.setText("98.6");
this.jFrame = new JFrame("TestText");
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
setText("new (button)");
}
});
}
public void setData(TestText data) {
data.setText("new (setData)");
}
public void getData(TestText data) {
}
public boolean isModified(TestText data) {
return false;
}
public void createGui(String[] args) {
jFrame.setContentPane(new TestText().frame);
jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jFrame.pack();
jFrame.setVisible(true);
}
}
and then here's the main class that I'm trying to create the gui from:
public class MainClass {
public static void main(String[] args) {
TestText gui = new TestText();
gui.createGui(null);
System.out.println(gui.getText());
gui.setData(gui);
System.out.println(gui.getText());
gui.setText("new (MainClass)");
System.out.println(gui.getText());
}
}
It looks like you're missing the reference to the text field I think...
gui.referenceToTextField.setText("new word");
EDIT: Very nice SSCCE! However, there are several problems (not in the order provided, necessarily).
You are overriding the setText() method. Don't do this unless you want the method to do something different—why you would want to do this I have no idea.
You aren't even using the args array in the createGui() method. You can create methods without specifying any parameters/arguments.
The getData() method is, right now, useless (If I were you, given what you're trying to accomplish, I would remove the method entirely). I'm assuming, from the apt method name (another good thing to do), that you want to retrieve the data from the text field. Put this line inside the method (and change the word void to String) and you should be set!
return textField.getText();
Truthfully, this shouldn't even run due to a NullPointerException. You aren't initializing any of the components other than the JFrame. You need to do things like textField = new JTextField(20).
Even if you could run this, the button wouldn't work at all because the button hasn't been told that it does anything. To do this call button.addActionListener() with the name of the listening class as the argument. If the GUI and listening classes happen to be in one class together (like I will show you in a minute), the argument is simply this.
You aren't adding any components to the frame. For every component you wish to put into your frame, you must call add(Component cmpt).
Having said this, I think I'm just going to try to recreate what you're trying to do here into one class. You don't really need two separate classes unless the listening portion is excessively long.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class TestText extends JFrame implements ActionListener {
JTextField textField = new JTextField(20);
JButton set = new JButton("Set Text");
JButton get = new JButton("Get Text");
public TestText() {
super();
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
add(textField);
set.addActionListener(this); //this tells the program that the button actually triggers an event
add(set);
get.addActionListener(this);
add(get);
pack();
setVisible(true);
}
public void actionPerformed(ActionEvent event) {
if (event.getSource() == set) {
textField.setText(JOptionPane.showInputDialog(null, "Enter a new word for the text field:"));
} else {
System.out.println(textField.getText());
}
}
public static void main(String[] args) {
TestText tt = new TestText();
}
}
After doing some reading I think it is due to my code not accessing the Event Dispatch Thread like #camickr suggested. Here is some documentation that helped me solve my problem.

Categories