How do I access an outer variable within inner nested methods? - java

I am aware that this question may already exist, and this is my first question on the site, so please bear with me.I still have difficulty understanding this in my case.
So here is the thing: I have a method that I call , and I have a button that will change the value of x. Depending on the value of x, I want the program to do something. The program below is not very complete but you will get the idea:
public class foo{
private void do(){
int x=0;
JButton changeValue= new JButton("Change the value of x");
changeValue.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e){
x=10; //change the x value when the button is clicked
//Here the user may also change the value of x
//by inputting some other number
}
});
//Something happens depending on x
//But nothing happens here because when I get the value of x,
//it reverts back to 0.
}
}
However, no matter where I declare my x in do(), I keep getting an error telling me that inner class cannot access outer class variables and that they must be declared final. But I can't declare it final because I need to change it later. I have tried putting the values in a new class. I have also tried declaring x as a member of foo() but that results to x
being 0 or null because for some reason once it exits the button click method it takes x to back to its old value: 0 or null.
What I need : when the button is pressed, the value of x is changed (Assuming that the user can change the value of x to some other number) Thanks for any answers in advance.

You need to create a final reference to your x variable.
Since it is primitive type, create a class to wrap it:
private static class MyX {
int x;
// + getter and setter
}
And then:
final MyX myX = new MyX(x);
JButton changeValue= new JButton("Change the value of x");
changeValue.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e){
myX.setX(10);
});
// get the value of MyX back if necessary

The problem is: when you create that ActionListener you are declaring an actionPerformed that will be executed sometime later. You cannot change x from actionPerformed because x exists only inside that method call.
class Foo {
void doSomething() {
int x = 0; // x inside doSomething
JButton btn = new Button("Do Something");
btn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent ae) {
/*
* This does not happen now:
* it happens later.
*
* By the time this happens
* the method will have exited
* and x will be gone.
*
*/
}
});
/* That x disappears forever here.
* (Or a little bit later but basically here.)
*/
}
}
You can declare x as final and then the anonymous class is given the value of it but you are wanting to change it. Probably what you are wanting is to make it a field:
class Foo {
int x = 0; // x inside Foo
JButton btn = new Button("Do Something");
Foo() {
btn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent ae) {
/* do something with x as needed */
}
});
}
}
Anonymous classes can also have fields:
btn.addActionListener(new ActionListener() {
int x = 0; // x inside anonymous ActionListener
#Override
public void actionPerformed(ActionEvent ae) {
/* use x only in actionPerformed */
}
});
"I have also tried declaring x as a member of foo() but that results to x being 0 or null because for some reason once it exits the button click method it takes x to back to its old value: 0 or null."
Unfortunately, this probably means you're doing something else wrong. I'm not sure what that might be from your description and code. For example, you're creating a new JButton locally inside a method which seems unusual. Unless it's one of those "createAndShowGUI" methods (as seen all over the Swing tutorials), you might be creating multiple buttons, multiple listeners, etc.

Related

Accessing local variable within actionPerformed Java eclipse

i am trying to reach the value within an int variable from within the actionPerformed class, the integer comes from another class where its passed as an argument "asd" and holds the value "1". I obviously deleted some lines from the code i posted below to make it easier to read but what i ve deleted has nothing to do with the problem im having.
So here, the output i see is;
asd
1
1
0
0
the last two outputs are from the actionPerformed class, they appear when i click the related button. How can i make it so those 2 outputs are also shown as 1?
public class customerAppointmentScreen extends JFrame implements ActionListener{
private JButton massB,indiB;
public int asd,id;
public customerAppointmentScreen(int asd) {
System.out.println("asd ");
System.out.println(asd);
id=asd;
System.out.println(id);
}
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
if(e.getSource()==massB) {
int id=asd;
System.out.println(asd);
System.out.println(id);
//this.setVisible(false);
}
else if(e.getSource()==indiB) {
this.setVisible(false);
}
}
}
Starting with...
public int asd,id;
public customerAppointmentScreen(int asd) {
System.out.println("asd ");
System.out.println(asd);
id=asd;
System.out.println(id);
}
The parameter asd is never assigned to the instance field asd, so the instance field remains 0 (id on the other hand is equal to the parameter asd)
When the ActionListener is triggered...
public void actionPerformed(ActionEvent e) {
if (e.getSource() == massB) {
int id = asd;
System.out.println(asd);
System.out.println(id);
//this.setVisible(false);
} else if (e.getSource() == indiB) {
this.setVisible(false);
}
}
You assign the instance field asd value to the local variable id, since asd is still zero, so is id.
The "simple" solution would be to assign the parameter from the constructor to the appropriate instance field OR use the correct instance field in the ActionListener, depending on your intentions.

Local variable a defined in an enclosing scope must be final or effectively final [duplicate]

I get the error, as in subject, and I kindly ask you how to repair it...
ERROR is in menuItem-loop, where I try to set the textArea foreground colour to one picked from menuItem: (colors[mi])
String[] colors = {
"blue",
"yellow",
"orange",
"red",
"white",
"black",
"green",
};
JMenu mnForeground = new JMenu("Foreground");
for (int mi=0; mi<colors.length; mi++){
String pos = Character.toUpperCase(colors[mi].charAt(0)) + colors[mi].substring(1);
JMenuItem Jmi =new JMenuItem(pos);
Jmi.setIcon(new IconA(colors[mi]));
Jmi.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
JMenuItem item = (JMenuItem) e.getSource();
IconA icon = (IconA) item.getIcon();
Color kolorIkony = getColour(colors[mi]); // ERROR HERE: (colors[mi])
textArea.setForeground(kolorIkony);
}
});
mnForeground.add(Jmi);
}
public Color getColour(String colour){
try {
kolor = Color.decode(colour);
} catch (Exception e) {
kolor = null;
}
try {
final Field f = Color.class.getField(colour);
kolor = (Color) f.get(null);
} catch (Exception ce) {
kolor = Color.black;
}
return kolor;
}
The error means you cannot use the local variable mi inside an inner class.
To use a variable inside an inner class you must declare it final. As long as mi is the counter of the loop and final variables cannot be assigned, you must create a workaround to get mi value in a final variable that can be accessed inside inner class:
final Integer innerMi = new Integer(mi);
So your code will be like this:
for (int mi=0; mi<colors.length; mi++){
String pos = Character.toUpperCase(colors[mi].charAt(0)) + colors[mi].substring(1);
JMenuItem Jmi =new JMenuItem(pos);
Jmi.setIcon(new IconA(colors[mi]));
// workaround:
final Integer innerMi = new Integer(mi);
Jmi.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
JMenuItem item = (JMenuItem) e.getSource();
IconA icon = (IconA) item.getIcon();
// HERE YOU USE THE FINAL innerMi variable and no errors!!!
Color kolorIkony = getColour(colors[innerMi]);
textArea.setForeground(kolorIkony);
}
});
mnForeground.add(Jmi);
}
}
Yes this is happening because you are accessing mi variable from within your anonymous inner class, what happens deep inside is that another copy of your variable is created and will be use inside the anonymous inner class, so for data consistency the compiler will try restrict you from changing the value of mi so that's why its telling you to set it to final.
What you have here is a non-local variable (https://en.wikipedia.org/wiki/Non-local_variable), i.e. you access a local variable in a method an anonymous class.
Local variables of the method are kept on the stack and lost as soon as the method ends, however even after the method ends, the local inner class object is still alive on the heap and will need to access this variable (here, when an action is performed).
I would suggest two workarounds :
Either you make your own class that implements actionlistenner and takes as constructor argument, your variable and keeps it as an class attribute. Therefore you would only access this variable within the same object.
Or (and this is probably the best solution) just qualify a copy of the variable final to access it in the inner scope as the error suggests to make it a constant:
This would suit your case since you are not modifying the value of the variable.
As I can see the array is of String only.For each loop can be used to get individual element of the array and put them in local inner class for use.
Below is the code snippet for it :
//WorkAround
for (String color : colors ){
String pos = Character.toUpperCase(color.charAt(0)) + color.substring(1);
JMenuItem Jmi =new JMenuItem(pos);
Jmi.setIcon(new IconA(color));
Jmi.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
JMenuItem item = (JMenuItem) e.getSource();
IconA icon = (IconA) item.getIcon();
// HERE YOU USE THE String color variable and no errors!!!
Color kolorIkony = getColour(color);
textArea.setForeground(kolorIkony);
}
});
mnForeground.add(Jmi);
}
}
One solution is to create a named class instead of using an anonymous class. Give that named class a constructor that takes whatever parameters you wish and assigns them to class fields:
class MenuActionListener implements ActionListener {
private Color kolorIkony;
public MenuActionListener(Color kolorIkony) {
this.kolorIkony = kolorIkony
}
#Override
public void actionPerformed(ActionEvent e) {
JMenuItem item = (JMenuItem) e.getSource();
IconA icon = (IconA) item.getIcon();
// Use the class field here
textArea.setForeground(kolorIkony);
}
});
Now you can create an instance of this class as usual:
Jmi.addActionListener(new MenuActionListener(getColor(colors[mi]));

What is the best practice for the scope of variables in a class

My teachers source code was published and i was confused when i saw this:
New local variables where instantiated from a static variable, the variable was then used in a method and passed as an argument to another method, to then set its new value to the same static variable that the copy was based on. Since the scope of a static variable will be accessible throughout the class, why not access the static variable directly from every method within that class?
Why do this:
public class Calculator {
private JTextField displayText;
public void actionPerformed(ActionEvent e) {
String input = displayText.getText();
if (something == right) {
for (int i = 0; i < 10; i++) {
enterNumber(input);
}
}
}
public void enterNumber(String input) {
displayText.setText(input);
}
}
If you can just:
public class Calculator {
private JTextField displayText;
public void actionPerformed(ActionEvent e) {
if (something == right) {
for (int i = 0; i < 10; i++) {
enterNumber();
}
}
}
public void enterNumber() {
String localVar = "Kitten";
displayText.setText(localVar);
}
}
In this exact example you are right, there is no point in having a parameter in your enter number method.
but when you begin to work on more complex programs you start to notice that you end up using the same functionality over and over again, in which you can have a method with parameters, such that you can perform the same action with slight variation.
also it is easier to read for other people if you have a method with parameters, since all you have to do is say, "ok i call the method and send it blank, it sets the value to whatever i sent it.", instead of saying "ok i call the method, now lets see what the method does."
if your code works then it is not bad code. but if it works and is easy to read then it is good code.

java Passing variables from one class to another

I have an error for the code below. Sorry if this is too basic as I am new to java.
Basically, I cannot retrieve the String "44418" from the class CityChange.
I know the reason is because I created a new instance cc in the class MainPanel.
However I do not know any other way of doing it.
public class CityChange extends JPanel {
public CityChange() {
JButton btn1 = new JButton("London")
this.add(btn1);
btn1.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent evt) {
//London Yahoo Weather Code 44418
setCitySelected("44418");
}
});
}
public void setCitySelected(String citySelected) {
this.citySelected = citySelected;
}
public String getCitySelected() {
return citySelected;
}
private String citySelected;
}
public class MainPanel extends JPanel {
public MainPanel() {
CityChange cc = new CityChange();
System.out.println(cc.getCitySelected()); //returns null instead of 44418
}
}
Please give some advice. Thank you.
For timing reasons, the value has no choice but to be null.
What happens here "immediately" (at init time) is that a new CityChange object is created and its citySelected is gotten and printed. As nobody set it otherwise, it is null.
Only after firing the event (clicking the button), it gets a value, and if you print the value then, you'll see the new value.
The code setCitySelected("44418"); is only executed when you call the method public void actionPerformed(ActionEvent evt) which is not happening at the moment. This Method is only called via a Button in a GUI so you first need at least a simple Window with a Button. Here is a good example http://docs.oracle.com/javase/tutorial/uiswing/events/actionlistener.html

How do I reference one method in another? And why am I only able to define a variable in an if statement once?

public void toggleoperator(View v){
ToggleButton oprtoggle = (ToggleButton) findViewById(R.id.operatortoggle);
boolean add = ((oprtoggle.isChecked()));
if (add){
oprtoggle.setTextOn("Add");
int x = 1;
} else {
oprtoggle.setTextOff("Subtract");
int x = -1;
}
public void sumNumbers(View v){
EditText input1 = (EditText) findViewById(R.id.input1);
int calc1 = Integer.parseInt(String.valueOf(input1.getText()));
EditText input2 = (EditText) findViewById(R.id.input2);
int calc2 = Integer.parseInt(String.valueOf(input2.getText()));
int totaladd = calc1 + calc2;
int totalsubtract = calc1 - calc2;
if (add) {
String result = String.valueOf(totaladd);
} else {
String result = String.valueOf(totalsubtract);
}
EditText output1 = (EditText)findViewById(R.id.output);
output1.setText(result);
}
I'm trying to get the above code to work. For one, I want to reference the boolean 'add' from the first method to the second method, how would I go about doing that? Also, I'm getting a 'Not a statement' error on this line
String result = String.valueOf(totalsubtract);
Help? And please explain too, otherwise I won't learn!
If you want to reference a method from another method you simply call the second method from the first one.
public void methodA(){
methodB();
Object o = new Object();
methodC(o);
}
public void methodB(){
//do something
}
public void methodC(Object o){
o.doSomething();
}
Fields in Java have something called scope. The scope of a local field, as in defined inside a method or a loop, is exhausted when the method or the loop is over.
If you want to make a field accessible to the whole Class, you need to define it outside methods, you do not need to initialize the field immediately, you can delegate it to constructor or methods.
public class Clazz{
public int i;
public Clazz(int i){
this.i = i;
}
public void setI(int i){
this.i = i;
}
}
If you're trying to access a variable in another method, it won't work because of "scope".
Basically, if you define a variable inside a method, it will only be available TO that method. One work around is to define the variables outside the method, so that all the methods in that class can access them. Another solution would be to "return" the variable, so that other methods can access them by calling your method.
I suggest reading this:http://docs.oracle.com/javase/tutorial/java/javaOO/accesscontrol.html
and this:http://java.about.com/od/s/g/Scope.htm
If you want your "add" boolean to be accessible from both methods, it should be created as a class variable, rather than a function variable OR it should be passed via parameters to the second function.
class Clazz {
//Declares the variable at class scope (this should be false by default)
boolean add;
//Sets the value of the "add" boolean
public void toggle () {
add = toggle.isChecked();
}
public void secondMethod () {
int diff = 0;
if (add) {
//make "diff" a positive value, based on what's entered.
} else {
//make "diff" a negative value, based on what's entered.
}
Display.setText(new String(diff));
}
}
I will not say that this is the nicest example code ever, however it is functional and SHOULD resolve your issues.
I would like to note, in addition, that it is trivial that the addition and subtraction both be done if only one of these is needed. This is why I had changed the way that the calculations come out.

Categories