I want to access the checkWord variable in the main code block. I don't know how to access it globally. How can I access a local variable in main in Java?
This is example code blocks.
textField.setOnAction(event -> {
try{
ArrayList<String> wordList = wordListReader();
boolean checkWord = false;
for(String word:wordList){
if(word.equals(textField.getText())){
checkWord = true;
}
}
System.out.println(checkWord);
}catch (FileNotFoundException ex){
ex.printStackTrace();
}
});
// Error
System.out.println(checkWord);
Design-wise, global variables (static fields in Java) are usually not a great idea because it causes a tight coupling between classes, making it harder to make changes to the system later on.
That said, to do what you describe you would do this:
public class YourClass {
// class field
public static boolean checkWord;
// instance (object) field
private TextField textField = new TextField("Your text field");
public void yourMethod() {
textField.setOnAction(event -> {
// ...
checkWord = true;
// ...
});
System.out.println(checkWord);
}
public static void iDoNotKnowAboutInstances() {
// OK
System.out.println(checkWord);
// Compile error - cannot refer to instance field in static context
System.out.println(textField);
}
}
Meanwhile, in another class:
public class YourOtherClass {
public void yourOtherMethod() {
System.out.println(YourClass.checkWord);
}
}
A static field exists at class level. It is initialized when the class is loaded by the class loader for the first time, in this case it will be initialized as false (the default for booleans). Then, when yourMethod is executed and an event is handled, the field checkWord is set to true. It can be referred to directly from within the same class. From another class it can be referred to by prefixing the class name, as shown in YourOtherClass.
EDIT: Not that you can refer to static fields from anywhere (as long as their visibility qualifier allows it) but you only refer to instance field via an actual instance. So for example from the static method iDoNotKnowAboutInstances you cannot refer to instance field textField. You often run into this when you create a simple java application with the entry method public static void main(String[] args). If you then add instance fields to the class you will first need to create an instance of the class using YourClass instance = new YourClass() to be able to read and write those fields.
Related
I'm confused about an essential thing in java.
public class InitItself1 {
public InitItself1(){}
private InitItself1 me = new InitItself1();
}
Of course I know that the StackOverFlowError will be occurred when creating an instance of the above class. The above class will be initiated recursively itself because of the initiation of the variable "me".
But,
public class InitItself2 {
public InitItself2(){}
private static InitItself2 me = new InitItself2();
}
Of course the outcome of the above class, "InitItself2" is different to the prior class, "InitItself1". This works just fine, no error occurred. As I know, initiating static variables and executing static blocks are performed when classes in which static variables and blocks are loaded.
What makes me confused is that I think it's the same that the variables, "me" of both classes, "InitItself1" and "InitItself2" are initiated, and also they have references to their classes in which they are, so it looks that "initiating recursively" would happen in initiating both classes.
What is the point that I'm missing?
Good answer please.
Thanks :)
You are not going to get StackOverFlowError in the second case. As you have said yourself, static variables are initiated when the class is loaded, and because a class is only loaded once, the static InitItself2 me will only be instantiated once. Creating a new object with constructor doesn't require the class to be reloaded.
public final class InitItself {
static {
System.out.println("Class is loaded");
}
private static InitItself me = new InitItself();
static {
System.out.println("me is instantiated");
}
public InitItself() {
System.out.println("Constructor called, me=" + me);
}
public static void main(String[] args) {
System.out.println("START");
InitItself i = new InitItself();
System.out.println("FINISH");
}
}
Gives the following output
Class is loaded
Constructor called, me=null
me is instantiated
START
Constructor called, me=oop.InitItself#6ff3c5b5
FINISH
I have some code that I need to reuse in several Java apps. That code implements a GUI which in turn needs to access some static variables and methods from the calling class. Those variables and methods are always called the same in all of the apps. Is there a generic way to obtain a handle to the calling class in Java so the code for "someGUI" class can remain untouched and in fact come from the same source file for all the different apps?
Minimal working example:
import javax.swing.*;
class test {
static int variable = 123;
public static void main(String[] args) {
someGUI sg = new someGUI();
sg.setVisible(true);
}
}
class someGUI extends JFrame {
public someGUI() {
System.out.println(String.format("test.variable = %d", test.variable));
}
}
How can I "generify" the reference to "test" in test.variable to always just refer to the calling class? It's not the "super" class, at least using super.variable doesn't work.
Firstly I would advise against this approach since there are only brittle ways to implement it. You should parameterize SomeGUI with a parameter containing the values you need instead.
However, it is possible to do what you ask by examining the thread's stack trace and using reflection to access the static fields by name. For example like this:
class Test {
static int variable = 123;
public static void main(String[] args) throws Exception {
SomeGUI sg = new SomeGUI();
}
static class SomeGUI extends JFrame {
public SomeGUI() throws Exception {
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
// stackTrace[0] is getStackTrace(), stackTrace[1] is SomeGUI(),
// stackTrace[2] is the point where our object is constructed.
StackTraceElement callingStackTraceElement = stackTrace[2];
String className = callingStackTraceElement.getClassName();
Class<?> c = Class.forName(className);
Field declaredField = c.getDeclaredField("variable");
Object value = declaredField.get(null);
System.out.println(String.format("test.variable = %d", value));
}
}
}
This will print test.variable = 123.
Obviously this is sensitive to renaming of the variables. It is also sensitive to dynamic proxies.
Also, it should be noted that you need to do this in the constructor. If you try to do this kind of lookup in other methods you can not find out how the instance was created.
There is no inheritance between somGUI and test,
Actual inheritance is there between someGUI and JFrame.
If you use super(), JVM tries to find 'variable' in JFrame, that is not what you wanted.
Use static methods setters & getters to access the 'variable' instead of direct accessing them.
This question already has answers here:
Cannot refer to a non-final variable inside an inner class defined in a different method
(20 answers)
Why are only final variables accessible in anonymous class?
(15 answers)
Closed 9 years ago.
Given the following inner class (IsSomething) within a method:
public class InnerMethod {
private int x;
public class Something {
private int y;
public void printMyNumber(double x)
{
class IsSomething extends Something {
public void print() {
System.out.println(x);
}
}
}
}
}
Why does the X variable has to be FINAL to make it work..?
(I'm talking ofc about the X parameter of the "printMyNumber" function.)
The difference is between local variables vs class member variables. A member variable exists during the lifetime of the enclosing object, so it can be referenced by the inner class instance. A local variable, however, exists only during the method invocation, and is handled differently by the compiler, in that an implicit copy of it is generated as the member of the inner class. Without declaring the local variable final, one could change it, leading to subtle errors due to the inner class still referring to the original value of that variable.
Final local variables
There are two reasons I know for making a local variable or a
parameter final. The first reason is that you don't want your code
changing the local variable or parameter. It is considered by many to
be bad style to change a parameter inside a method as it makes the
code unclear. As a habit, some programmers make all their parameters
"final" to prevent themselves from changing them. I don't do that,
since I find it makes my method signature a bit ugly.
The second reason comes in when we want to access a local variable or
parameter from within an inner class. This is the actual reason, as
far as I know, that final local variables and parameters were
introduced into the Java language in JDK 1.1.
public class Access1 {
public void f() {
final int i = 3;
Runnable runnable = new Runnable() {
public void run() {
System.out.println(i);
}
};
}
}
Inside the run() method we can only access i if we make it final in the outer class. To understand the reasoning, we have to
look at what the compiler does. It produces two files, Access1.class
and Access1$1.class. When we decompile them with JAD, we get:
public class Access1 {
public Access1() {}
public void f() {
Access1$1 access1$1 = new Access1$1(this);
}
}
and
class Access1$1 implements Runnable {
Access1$1(Access1 access1) {
this$0 = access1;
}
public void run() {
System.out.println(3);
}
private final Access1 this$0;
}
Since the value of i is final, the compiler can "inline" it into the inner
class. It perturbed me that the local variables had to be final to be
accessed by the inner class until I saw the above.
When the value of the local variable can change for different
instances of the inner class, the compiler adds it as a data member of
the inner class and lets it be initialised in the constructor. The
underlying reason behind this is that Java does not have pointers, the
way that C has.
Consider the following class:
public class Access2 {
public void f() {
for (int i=0; i<10; i++) {
final int value = i;
Runnable runnable = new Runnable() {
public void run() {
System.out.println(value);
}
};
}
}
}
The problem here is that we have to make a new local data member each time we go through the for loop, so a thought I had today
while coding, was to change the above code to the following:
public class Access3 {
public void f() {
Runnable[] runners = new Runnable[10];
for (final int[] i={0}; i[0]<runners.length; i[0]++) {
runners[i[0]] = new Runnable() {
private int counter = i[0];
public void run() {
System.out.println(counter);
}
};
}
for (int i=0; i<runners.length; i++)
runners[i].run();
}
public static void main(String[] args) {
new Access3().f();
}
}
We now don't have to declare an additional final local variable. In fact, is it not perhaps true that
int[] i is like a common C pointer to an int? It took me 4 years to
see this, but I'd like to hear from you if you have heard this idea
somewhere else.
The methods in an anonymous class don't really have access to local variables and method parameters. Rather, when an object of the anonymous class is instantiated, copies of the final local variables and method parameters referred to by the object's methods are stored as instance variables in the object. The methods in the object of the anonymous class really access those hidden instance variables.
From the JLS :
Any local variable, formal method parameter or exception handler parameter used but not declared in an inner class must be declared final. Any local variable, used but not declared in an inner class must be definitely assigned (ยง16) before the body of the inner class.
This is because the lifetime of an instance of a local class can be much longer than the execution of the method in which the class is defined. For this reason, a local class must have a private internal copy of all local variables it uses (these copies are automatically generated by the compiler). The only way to ensure that the local variable and the private copy are always the same is to insist that the local variable is final.
I have the following two classes:
public class Class1
{
public Class1 randomvariable; // Variable declared
public static void main(String[] args)
{
randomvariable = new Class1(); // Variable initialized
}
}
public class Class2
{
public static void ranMethod()
{
randomvariable.getSomething(); // I can't access the member "randomvariable" here even though it's public and it's in the same project?
}
}
I am very certain that it's a very fundamental thing I'm missing here, but what am I actually missing? The Class1 member "randomvariable" is public and so is the class and both classes are in the same project.
What do I have to do to fix this problem?
There are two problems:
Firstly, you're trying to assign a value to randomvariable from main, without there being an instance of Class1. This would be okay in an instance method, as randomvariable would be implicitly this.randomvariable - but this is a static method.
Secondly, you're trying to read the value from Class2.ranMethod, again without there being an instance of Class1 involved.
It's important that you understand what an instance variable is. It's a value associated with a particular instance of a class. So if you had a class called Person, you might have a variable called name. Now in Class2.ranMethod, you'd effectively be writing:
name.getSomething();
That makes no sense - firstly there's nothing associating this code with Person at all, and secondly it doesn't say which person is involved.
Likewise within the main method - there's no instance, so you haven't got the context.
Here's an alternative program which does work, so you can see the difference:
public class Person {
// In real code you should almost *never* have public variables
// like this. It would normally be private, and you'd expose
// a public getName() method. It might be final, too, with the value
// assigned in the constructor.
public String name;
public static void main(String[] args) {
Person x = new Person();
x.name = "Fred";
PersonPresenter.displayPerson(x);
}
}
class PersonPresenter {
// In a real system this would probably be an instance method
public static void displayPerson(Person person) {
System.out.println("I present to you: " + person.name);
}
}
As you can tell by the comments, this still isn't ideal code - but I wanted to stay fairly close to your original code.
However, this now works: main is trying to set the value of an instance variable for a particular instance, and likewise presentPerson is given a reference to an instance as a parameter, so it can find out the value of the name variable for that instance.
When you try to access randomvariable you have to specify where it lives. Since its a non-static class field, you need an instance of Class1 in order to have a randomvariable. For instance:
Class1 randomclass;
randomclass.randomvariable.getSomething();
If it were a static field instead, meaning that only one exists per class instead of one per instance, you could access it with the class name:
Class1.randomvariable.getSomething();
I am trying to access a form which is not static from another class which is also not static. I'd like to use a member in the class....
Public Class MainForm
public void setConsoleText(String Text){
jTextArea1.append(Text);
}
I need to know a way to reference this setter from my class "Log" which is basically where data goes to be parsed and logged. I want it to be like this:
private void consoleOut(String data) {
System.out.println(data);
MainForm.setConsoleText("data");
}
I cannot access this method.. I can only access MyForm.Class. Is there a way to reference the one that's been instantiated, or all of them in this virtual machine? It really doesn't matter as there will only be one of these running in this instance of the Java VM.
I just can't seem to figure this one out.
You need to give Log a non-static MainForm variable and pass reference to the currently visualized MainForm object into the Log class and into this variable. This can be done via a Log constructor parameter or via a setter method. Then you can call methods on this instance (but checking that it's not null first). Something like:
public class Log {
private MainForm mainForm; // our MainForm variable
public Log(MainForm mainForm) {
// setting the MainForm variable to the correct reference in its constructor
this.mainForm = mainForm;
}
private void consoleOut(String data) {
System.out.println(data);
if (mainForm != null) {
// now we can use the reference passed in.
mainForm.setConsoleText("data");
}
}
}
Edit 1
For instance if you create your MainForm object and display it from a main method somewhere, create Log along with it and pass the visualized MainForm into the Log constructor, something like so:
public static void main(String[] args) {
MainForm myMainForm = new MainForm();
// ... whatever code is necessary to set up the
// ... MainForm object so it can be visualized
myMainForm.setVisible(true); // and show it
Log myLogObject = new Log(myMainForm);
//...
}
Note that if this doesn't help you, you'll need to post more of your code.