I will be amazed if anyone can answer this question. I am a beginner struggling immensely with the syntax and logic of nested classes in Java. If you run the following program, 'a' will print instead of 'b'. Why?
class MainClass
{
public static void main(String[] args)
{
Outer OuterRefVar_a = new Outer('a');
Outer OuterRefVar_b = new Outer('b');
OuterRefVar_a.InnerTypeMember = OuterRefVar_a.new Inner();
OuterRefVar_b.InnerTypeMember = OuterRefVar_a.InnerTypeMember;
OuterRefVar_b.InnerTypeMember.set_innerChar_to_outerChar();
System.out.println(OuterRefVar_b.InnerTypeMember.innerChar);
}
}
class Outer
{
char outerChar;
Outer(char outerChar)
{
this.outerChar = outerChar;
}
class Inner
{
char innerChar;
void set_innerChar_to_outerChar()
{
innerChar = outerChar;
}
}
Inner InnerTypeMember;
}
That happens because while you have set the InnerTypeMember reference of object of A onto B..
OuterRefVar_b.InnerTypeMember = OuterRefVar_a.InnerTypeMember;
The inner object of A still has a reference to it's original Outer object A and will reference its member variables. Java implements inner classes by giving the object a secret reference to "Outer.this" which doesn't change simply by setting the InnerTypeMember on the other instance.
For example, if you had a InnerTypeMember variable within a completely different class, calling set_innerChar_to_outerChar() would still be expected to find Outer.outerChar on the object for which the inner class was original constructed.
Related
I am preparing for Java 11 certification and while revising Java concepts, I got trapped in one of the silly concept.
I know that OuterClass's instance fields could be accessed from an InnerClass using OuterClass.this.
Please help me to understand why we cannot re-assign a value to OuterClass's instance fields inside an InnerClass (outside a method or block).
public class OuterClass {
String outerInstanceField = "Outer instance field"; // Instance Field
class InnerClass {
OuterClass.this.outerInstanceField = "Inside Inner Class, now";
}
}
Above code throws error:
However, this works perfectly fine if the re-assignment is done either in a block or in the InnerClass method.
public class OuterClass {
String outerInstanceField = "Outer instance field"; // Instance Field
class InnerClass {
{
OuterClass.this.outerInstanceField = "Inside Inner Class, now";
System.out.println(OuterClass.this.outerInstanceField);
}
void print(){
OuterClass.this.outerInstanceField = "Inside Inner Class's method, now";
System.out.println(OuterClass.this.outerInstanceField);
}
}
public static void main(String[] args) {
OuterClass outerObject = new OuterClass();
InnerClass innerObject = outerObject.new InnerClass();
innerObject.print();
}
}
Output:
Nested Class class example
class OuterClass {
static String outerInstanceField = "Outer instance field"; // Static/Class Field
static class InnerClass {
OuterClass.outerInstanceField = "Inside nested Class, now"; // Compiler error
static {
OuterClass.outerInstanceField = "Inside nested Class's static block, now"; // works fine
System.out.println(OuterClass.outerInstanceField);
}
}
}
I might be wrong here, but where you first have this assignment is a completely non-dynamic (for lack of better word and because I don't want to use the in this context ambiguous word 'static') area. There you would define things about the current scope, like which fields this class has or which methods.
That's why the assignment to String outerInstanceField works, as you are in the Scope of the OuterClass and are defining how it is defined.
What you are doing in the Innerclass is actually dynamic, i.e. something that would be executed. But you cannot even tell, when. Is it at the time of class loading? When some object is created? You have no information about that.
You also cannot put for example an if or a loop there, which basically is the same.
In this second snippet you could for example also try to add a static before the block. This will also not work, because it will be executed when the class is loaded and then you cannot even have an instance of OuterClass.
Edit, as response to your comment: It would be nice if you added a small example of what you mean, so I can be sure but if I understand correctly, the problem is the following: The InnerClass in your example is not static. This means, it will always belong to exactly one instance of OuterClass. This is, why OuterClass.this is unambiguous and works. On the other hand, there can be an arbitrary number of InnerClass-Objects for one OuterClass Instance, i.e. InnerClass.this can not be determined uniquely and therefore does not work.
Considering below scenario:
public class A{
int x = 10;
}
class B extends A{
x = 0;
int y = x;
}
If we execute above code, we receive compiler error:
We cannot have isolated expressions outside the scope of a constructor, a method, or an instance/static initialization block.
So, the questions narrows down to whether variable assignment is an expression or statement.
In The Java™ Tutorials, under topic Expressions, Statements, and Blocks it is mentioned that assignment and declaration are statements.
Also,
Learning Java, by Patrick Niemeyer, Daniel Leuck
mentions,
Technically, because variable assignments can be used as values for further assignments or operations (in somewhat questionable programming style), they can be considered to be both statements and expressions.
So, I am considering it as special case of expression.
I cannot access var variable from inner class method. but java is accessing to class variable instead of local variable. How can I access to local variable instead of class variable from inner class method ?
class Foo {
void test() {
int var = 3;
class Inner {
int var = 1;
void print_var_of_test() {
System.out.println(var); // this line prints 1 why ?
// I want this line to print var of test (3) function.
}
}
}
}
You cannot access a local variable defined in a method of the outer class from an inner class if the inner class defines already a variable with the same name.
You could use distinct names to distinguish them.
As workaround to keep the same variable names, you could change the signature of print_var_of_test() to accept an int.
In this way, outside the inner class you could pass the int var variable as the method is invoked.
class Foo {
void test() {
int var = 3;
class Inner {
int var = 1;
void print_var_of_test(int var) {
System.out.println("outer var=" + var);
System.out.println("inner var=" + this.var);
}
}
Inner inner = new Inner();
inner.print_var_of_test(var);
}
}
You do that by picking another name for either the local variable or the inner class variable.
Since the inner class and the method variable are defined in the same source method, you should always be able to change one of them. There is never a situation in which you don't have the permission/right/capability to change one but not the other.
What about naming inner class var as varInner if you want to compare between it and the original one?
I'd like to ask when it's required to explicitly write the name of the outer class when referring to the inner class or static nested class.
Ex:
class B {
static class Inner {
int a = 10;
}
}
class Test extends B {
public static void main(String[] args) {
Test t = new Test();
Inner obj = new Inner();
System.out.println(obj.a);
}
}
What I'd like to ask is under which circumstances it's needed to write:
B.Inner obj = new B.Inner();
instead of
Inner obj = new Inner();
For static nested classes (as G. Fiedler has mentioned in his answer) you can write new Inner() when you are within the scope of B.
You can also directly refer to Inner from within Test because it extends B.
In all other cases you need to say B.Inner and new B.Inner().
Inner classes - which are not static by definition - are initialized differently. Let's look at this example B:
class B {
public class Inner {
...
}
}
Inner classes are always tied to an instance of the outer class. That means that in this case there has to be an instance of B for us to be able to instantiate Inner. So let's do that:
B b = new B();
I'm by the way assuming we are currently somewhere outside of the scope of B
In order to create an instance of Inner we use our instance of B and write:
B.Inner inner = b.new Inner();
If we were inside of B (in a non-static method) we could, however, simply write
Inner inner = new Inner();
because we - or rather the compiler - know(s) that an instance of B has to exist for this inner to be tied to.
If you are inside the scope of class B then new Inner() can be used. Outside of class B new B.Inner() must be used.
Master test = new Inner();
System.out.println(test.getClass());
In the above example the Inner class extends the Master class, but what I'm confused about is that test.getClass() returns Inner, but isn't test really of the type Master? Other than the constructor no methods/properties can be used from the Inner class, only what's in the Master class. Furthermore the constructor for Inner actually sets properties exclusive to Inner, but somehow these properties don't exist in test even though it uses the constructor -- which doesn't seem like it should work.
For example if define the classes as:
public class Master {
public int number = 0;
public Master() {
number = 9;
}
}
public class Inner extends Master {
public int innerNumber = 0;
public Inner() {
number = 1;
innerNumber = 2;
}
}
test will use Inner's constructor which sets innerNumber, but test.innerNumber doesn't even exist because innerNumber isn't apart of the Master type. Also, test.getClass() says it's of the Inner type, not Master.
Object.getClass() returns the class object of the dynamic type of the object, not the static type (the type of the variable or attribute you declared it).
Hence new Inner().getClass() returns Inner.class, new Master().getClass() returns Master.class no matter what the type of the variable is that holds the reference.
Question 1:
Master test = new Inner();
The above line indicates that get method implementation's from Inner class (ovveriding). So Inner classes getClass() method calls.
Question 2:
test.innerNumber
Inheritance happens from Parent to Child. innerNumber is a property of Inner(child). Master(Parent) won't get it.
I have a query regarding accessibility of top level class from member inner class.
I have just read the reason why local or anonymous inner classes can access only final variables.The reason being JVM handles these two classes as entirely different classes and so, if value of variable in one class changes, it can't be reflected at run time in another class file.
Then, my question is that how an inner member class (non-static) can have access to members to members of top level class, as JVM is still treating these two classes as different class files? If value of a member variable of top level class changes, how will it possible to reflect in class file of inner class at runtime?
They're separate classes, but there's an implicit reference to the instance of the "outer" class in the "inner" class. It basically acts as a variable which you can get at either implicitly or via special syntax of ContainingClassname.this.
Note that if you don't want such an implicit reference, you should declare the nested class as static:
public class Outer
{
private class Inner
{
// There's an implicit reference to an instance of Outer in here.
// For example:
// Outer outer = Outer.this;
}
private static class Nested
{
// There's no implicit reference to an instance of Outer here.
}
}
this is implicitly final, you cannot change it. When you write some thing like
class Outer {
int a;
class Inner {
{ a = 1; }
}
}
you are actually writing the same as
class Outer {
int a;
class Inner {
{ Outer.this.a = 1; }
}
}
The a is not final but the Outer.this is, and that is the reference which is used.