Professional Documents
Culture Documents
SCJP 6.0
SCJP 6.0
Genius("Java");
Monday, September 8, 2008
Static Inner Classes
If you are astonished by the above list of information, don't worry. In the
course of this tutorial, you will come across all these facts.
Now let's begin one by one how the compiler changes different types of codes
in an inner class. By the end you will know completely how all the code in an
inner class is transformed to make the inner class a normal top level class.
First lets see how an inner class accesses static members of the enclosing class
by their names.
Code
1. class Outer
2. {
3. static int a = 10;
4. static class Inner
5. {
6. void display()
7. {
8. System.out.println(a); //displays 10
9. }
10. }
11. }
Now when you compile the program the compiler converts the class Inner to a
normal top level class. But now the question arises that what will happen to
Statement # 8. Well the answer is simple, the compiler adds the name of the
enclosing class i.e. Outer before the name of the variable a. So after compilation,
the program given above will look like this-
Code after Comilation
1. class Outer
2. {
3. static int a = 10;
4. }
5. class Outer$Inner
6. {
7. void display()
8. {
9. System.out.println(Outer.a); //displays 10
10. }
11. }
Note the name of the member(inner) class after compilation. The compiler adds
the name of the enclosing class before the name of the member class seperated
by a $. The static keyword is also gone as top-level classes cannot be static. To
access the member class from any code outside the enclosing class, you have to
use the syntax <enclosingClassName>.<memberClassName>. So to access the class Inner
from outside the class Outer you will have to use the name of the class as
Outer.Inner.
Now you know that members of a class can access private members of the class.
So a member class can also access the private members of the enclosing class. But
how can the member class access private members of the enclosing class after
compilation. Since after compilation the member class is converted into a
normal top-level class, so it has no direct access to the private members of the
enclosing class. But the compiler creates some package visible methods to make this
possible.
Code
1. class Outer
2. {
3. private static int a = 10;
4. static class Inner
5. {
6. void display()
7. {
8. System.out.println(a); //displays 10
9. }
10. }
11. }
Now after compilation the class Inner will become a top-level class. At Statement #
8 the compiler adds a method call since this statement tries to access a private
member of the enclosing class. Lets see how this happens-
Code after Compilation
1. class Outer
2. {
3. private static int a = 10;
4. static int access$000()
5. {
6. return a;
7. }
8. }
9. class Outer$Inner
10. {
11. void display()
12. {
13. System.out.println(Outer.access$000()); //displays 10
14. }
15. }
Look how cleverly the compiler generates a method at Statement # 4 which has
access to private members of the class. This method returns the value of the
static field a. Notice the ugly name of the compiler generated method. You
cannot explicitly call this method in your code because the compiler will flag it
as an error. If a local declaration or a field in the inner class or its super class
hides the field of the enclosing class, you can access the field by placing the
name of the enclosing class before the field name. It goes like this-
Code
1. class Outer
2. {
3. static int a = 10;
4. private static int b = 20;
Code
5. static class Inner
6. {
7. String a;
8. String b;
9. void display()
10. {
11. System.out.println(Outer.a); //displays 10
12. System.out.println(Outer.b); //displays 20
13. }
14. }
15. }
Now that you know everything about how a Static Member Class can access all
the static members of the enclosing class, lets see how the static member class
accesses the non-static members of the enclosing class on an object of the
enclosing class.
Code
1. class Outer
2. {
3. int a = 10;
4. private int b = 20;
5. static class Inner
6. {
7. void display()
8. {
9. Outer outer = new Outer();
10. System.out.println(outer.a); //displays 10
11. System.out.println(outer.b); //displays 20
Code
12. }
13. }
14. }
In this case the compiler generates a static package visible method at Statement #
5 which takes an object of the enclosing class as parameter. Statement # 16
accesses the private non-static member of the enclosing class by calling this
method and sending the instance of the enclosing class as argument. The
method returns the value of the field on the object passed to it.
Till now you have only seen how to access the value of fields from an Inner
Class. But you can also mutate i.e. set the value of fields and make method
calls. The example below shows you how this happens for non-private members-
Code
1. class Outer
2. {
3. int a = 10;
4. static int b = 20;
5. int getProduct()
6. {
7. return a * b;
8. }
9. static int getCube()
10. {
Code
11. return b * b * b;
12. }
13. static class Inner
14. {
15. void display()
16. {
17. b = 2;
18. System.out.println(b); //displays 2
19. System.out.println(getCube()); //displays 8
20. Outer outer = new Outer();
21. outer.a = 100;
22. System.out.println(outer.getProduct()); //displays
23. 200
24. System.out.println(outer.a); //displays
25. 100
26. }
}
}
There is not much changes made in this code by the compiler. So the code after
compilation would look as-
The compiler adds the name of the class Outer before the field and method
names at Statements # 18, 19 and 20. This is the only change made by the compiler
(apart from making the class Inner a top level class). Its easy enough! But if what
we are trying to access is private member of the enclosing class, then the code
becomes a bit complicated. Lets see how you can mutate the values of private
fields of the enclosing class-
Code
1. class Outer
2. {
3. private int a = 10;
4. private static int b = 20;
5. static class Inner
6. {
7. void display()
8. {
9. b = 2;
10. Outer outer = new Outer();
11. outer.a = 100;
12. }
13. }
14. }
For Statement # 9 the compiler will generate a static package visible method that
takes the value that you set with the assignment. For Statement # 11 the
compiler generates another static package visible method that takes the object
of the class Outer and the value that you assign to the field. These methods are
shown at Statements # 5 and 9 in the code below-
Code after Compilation
1. class Outer
2. {
3. private int a = 10;
4. private static int b = 20;
5. static void access$000(int val)
6. {
7. b = val;
8. }
9. static void access$100(Outer outer, int val)
10. {
11. outer.a = val;
12. }
13. }
14. class Outer$Inner
15. {
16. void display()
17. {
18. Outer.access$000(2); //b = 2;
19. Outer outer = new Outer();
20. Outer.access$100(outer, 100); //outer.a = 100;
21. }
22. }
The statements at Statements # 18 and 20 are generated by the compiler. The original
statements are shown in comments. Now lets see how you can call private methods of the
enclosing class-
Code
1. class Outer
2. {
3. private void getProduct(int x, int y)
4. {
5. System.out.println(x * y);
6. }
7. private static int getCube(int x)
8. {
9. return x * x * x;
10. }
11. static class Inner
12. {
13. void display()
14. {
15. System.out.println(getCube(3)); //displays 9
16. Outer outer = new Outer();
17. outer.getProduct(10,20); //displays 200
18. }
19. }
20. }
For both these methods, the compiler generates static package visible methods
which inturn calls these methods which are unavailable to the inner class after
compilation. The methods generated by the compiler for the mehods and their call are
shown in the code below-
Code after Compilation
1. class Outer
2. {
3. private void getProduct(int x, int y)
4. {
5. System.out.println(x * y);
6. }
7. private static int getCube(int x)
8. {
9. return b * b * b;
10. }
11. static int access$000(int x)
12. {
13. return getCube(x);
14. }
15. static void access$100(Outer outer, int x, int y)
16. {
17. outer.getProduct(x, y);
18. }
19. }
20. class Outer$Inner
21. {
22. void display()
23. {
Code after Compilation
24. System.out.println(Outer.access$000(3));
25. //System.out.println(getCube(3));
26. Outer outer = new Outer();
27. Outer.access$100(outer, 10, 20);
28. //outer.getProduct(10, 20);
}
}
The methods at Statements # 11 and 15 call the methods getCube() and getProduct()
respectively. The methods calls at Statements # 24 and 27 are generated by the
compiler. The original calls are shown in comments. Lets see the opposite of
this i.e. accessing the private members of the inner class from the members of the
enclosing class.
Code
1. class Outer
2. {
3. static class Inner
4. {
5. static int staticVal = 10;
6. int nonPrivateVal = 20;
7. private int privateVal = 30;
8. void nonPrivateDisplay()
9. {
10. System.out.println("Non-Private Display!");
11. }
12. private void privateDisplay()
13. {
14. System.out.println("Private Display!");
15. }
16. }
17. void display()
18. {
19. System.out.println(Inner.staticVal); //displays 10
20. Inner in = new Inner();
21. System.out.println(in.nonPrivateVal); //displays 20
22. System.out.println(in.privateVal); //displays 30
23. in.nonPrivateDisplay(); //displays Non-
24. Private Display!
25. in.privateDisplay(); //displays
26. Private Display!
}
}
The compiler will generate package visible methods for the access statements
at Statements # 22 and 24. So the code would look like this-
Code after Compilation
1. class Outer
2. {
3. void display()
4. {
Code after Compilation
System.out.println(Inner.staticVal); //displays
10
Outer.Inner in = new Outer.Inner();
5.
System.out.println(in.nonPrivateVal); //displays
6.
20
7.
System.out.println(Outer.Inner.access$000(in)); //displays
8.
30
9.
in.nonPrivateDisplay(); //displays
10.
Non-Private Display!
11.
Outer.Inner.access$100(in); //displays
12.
Private Display!
13.
}
14.
}
15.
class Outer$Inner
16.
{
17.
static int staticVal = 10;
18.
int nonPrivateVal = 20;
19.
private int privateVal = 30;
20.
void nonPrivateDisplay()
21.
{
22.
System.out.println("Non-Private Display!");
23.
}
24.
private void privateDisplay()
25.
{
26.
System.out.println("Private Display!");
27.
}
28.
static int access$000(Outer$Inner inner)
29.
{
30.
return inner.privateVal;
31.
}
32.
static void access$100(Outer$Inner inner)
33.
{
34.
inner.privateDisplay();
}
}
Note that I have used the name Outer.Inner to represent the inner class in the
enclosing class at Statements # 6, 8 and 10. This is just to avoid any confusion as
the name Outer.Inner must be used to represent the inner class from any code
outside the enclosing class. There is one last thing that you must note before
we move to Non Static Member Classes. If you don't access some members of
the enclosing class from the inner class then the compiler doesn't generates methods
for them. For Example-
Code
1. class Outer
2. {
3. static int a = 10;
4. static class Inner
5. {
6. void display()
7. {
Code
8. System.out.println("Hello!");
9. }
10. }
11. }
The code above will not be altered much by the compiler. Since the field a is
not accessed in the inner class so no method will be generated for that. So the code
after compilation will look like this-
Code after Compilation
1. class Outer
2. {
3. private static int a = 10;
4. }
5. class Outer$Inner
6. {
7. void display()
8. {
9. System.out.println("Hello!");
10. }
11. }
I think now you know about Static Member classes from inside out. I hope this
will help you a lot. View my other posts to learn more about the other forms of
Inner Classes.
Posted by Ankit Garg at 5:09 AM 5 comments
Labels: Changes made to Inner Classes by the Compiler, Inner Classes in Java, Java Inner Classes after Compilation, Static Inner Classes in Java, Static Member
Classes
Non-Static Member
Classes
Lets again start with the introduction of Non-Static Member Classes.
If you have not read how Static Member Classes work, then I recommend that
you read Static Member Classes first before continuing. This is because I will
not re-mention how a member class accesses static members including private
static members of the enclosing class.
The main difference between Static Member Class and Non-Static Member
class is that the every object of a Non-Static Member Class has an object of the
enclosing class stored within it. This is why you can access non-static members of
the enclosing class in a Non-Static Member Class. But how does this happens.
See the code below which shows a simple example of how to create an instance
of a non-static inner class.
Code
1. class Outer
2. {
3. class Inner
4. {
5. }
6. void display()
7. {
8. Inner in = new Inner();
9. }
10. }
At Statement # 8 you can see that an instance of the non-static member class has
been created. But the question still remains that how does that instance which
is created at Statement # 8 has an instance of the enclosing class in it. If you
have studied inner classes closely than you must be knowing that the code at
Statement # 8 implicitly uses the this reference to create the inner class instance.
So the code implicitly looks like this-
Code
1. class Outer
2. {
3. class Inner
4. {
5. }
6. void display()
7. {
8. Inner in = this.new Inner();
Code
9. }
10. }
You may have noticed this if you mistakenly typed this code in you program-
Code
1. class Outer
2. {
3. class Inner
4. {
5. }
6. public static void main(String[] args)
7. {
8. Inner in = new Inner();
9. }
10. }
You get this error because the compiler will asume that you want to create the
instance of the inner class with the this reference. So to create
an instance of the inner class from a static method, you have to explicitly use an
instance of the enclosing class like this-
Code
1. class Outer
2. {
3. class Inner
4. {
5. }
6. public static void main(String[] args)
7. {
8. Outer out = new Outer();
9. Inner in = out.new Inner();
10. }
11. }
Now lets come to the point. What's the use of this enclosing class instance?
Well the instance of the enclosing class that you use with the new operator is
passed to the constructor of the inner class. It doesn't matter whether you
provide a constructor for the inner class or not. The compiler adds the object
of the enclosing class as the last parameter of the inner class. The compiler
also adds some code to the inner class constructor that stores the object of the
enclosing class passed to it into a field that stores the reference of the conclosing class.
This field is also added to the class by the compiler. Lets see the code generated by the
compiler for the class that we created above-
This final reference of the enclosing class is used to access the non-static
members of the enclosing class. The non static inner class accesses the static
members of the enclosing class in the same way as static inner class accesses
them. We will see here how it accesses the non-static members of the enclosing
class. See the code below-
Code
1. class Outer
2. {
3. int a = 10;
4. private int b = 20;
5. class Inner
6. {
7. void display()
8. {
9. System.out.println(a);
10. System.out.println(b);
11. }
12. }
13. }
Remember how the static inner class accessed the non-static members of the
enclosing class on an instance of the enclosing class. The non-static inner class
will also access the non-static members of the enclosing class in the same way. The
difference will be that an implicit reference to the enclosing class will be used. So the
code after compilation would look like this-
Look how cleverly the compiler utilizes the enclosing class instance at
Statements # 18 and 19. Again here the compiler generates an accessor method at
Statement # 5 so that the private member b can be accessed by the access
statement in the display() method in the inner class. But what will happen if a
declaration in the inner class or it's super class hides the enclosing class' instance
member. You might know that in such a case a special form of this reference is
used. Lets see the special form of this reference in action.
Code
1. class Outer
2. {
3. int a = 10;
4. class Inner
5. {
6. int a = 100;
7. void display()
8. {
9. System.out.println(a);
10. System.out.println(Outer.this.a);
Code
11. }
12. }
13. }
The special syntax of the this reference can be seen at Statement # 10. The rule
behind it is simple. This syntax of the this
reference (<enclosing class name>.this) represents the enclosing class instance
stored by the compiler in the non-static inner class. Look how
the Statement # 10 in the code above is changed into Statement # 15 in the code
below-
The code must have made it clear to you that the special syntax of the this
reference is just a trick to use the final enclosing class reference which is not
available before compilation. There is nothing more in non-static inner classes.
The enclosing class members can access the members of a non-static inner
class just as they access the members of a static inner class. The only
difference is that to create an instance of a non-static inner class, you need an
instance of the enclosing class while enclosing class instance is not required to
create an instance of a static inner class.
Posted by Ankit Garg at 4:43 AM 0 comments
Labels: Changes made to Inner Classes by the Compiler, How Inner Classes Work, Inner Classes Tutorial Java, Java Inner Classes after Compilation, Non-Static
Member Classes, Problem with Inner Classes
Local Classes
Static Local Classes
As always, lets begin with an introduction of Static Local Classes-
If you have not read how Static Member Classes and Non-Static Member Classes
work, then I recommend that you read Static Member Classes and Non-Static
Member Classes first before continuing.
Now when you compile the program, the compiler generates a constructor for
the class Local and adds two int variables as parameters to the constructor. At
Statement # 14 the compiler adds a and b as arguments to the constructor call. Lets see
the modified code-
Code after Compilation
1. class Outer
2. {
3. static void containerMethod(final int a)
4. {
5. final int b = 10;
6. Outer.Local local = new Outer.Local(a, b);
7. }
8. }
9. class Outer$Local
10. {
11. final int val$a;
12. final int val$b;
13. Outer$Local(int a, int b)
14. {
15. val$a = a;
16. val$b = b;
17. }
18. void display()
19. {
20. System.out.println(val$a); //System.out.println(a);
21. System.out.println(val$b); //System.out.println(b);
22. }
23. }
You might be asking why didn't the compiler replace the name of constants
with the actual value of the constants. Well the compiler could have done that
for b as it is known at compile time that the value of b is 10. But what if b was
declared and initialized in separate statements. Then b would have not been a
compile time constant. What about a? Its value is not known at compilation
time. The value will be passed as a parameter. This is why compiler caches the
final local constants in a local class instead of replacing the name of the constant
with the value of the constant.
The local class can only access the non-hidden final local variables. If a
declaration in the local class or its super class hides the final local variables, they
become inaccessible. There is no way to access them. For Example-
Code
1. class Outer
2. {
3. static void containerMethod()
4. {
5. final int a = 10;
6. class Local
7. {
8. int a = 100; //hides local a
9. void display()
10. {
11. System.out.println(a); //displays 100
12. }
13. }
14. Local local = new Local();
15. }
16. }
You might have guessed it what the compiler will do here. At Statement # 15 it
will add this reference as a parameter to the constructor. The Local class will
store the this reference with itself and use it to access a and b.
Code after Compilation
1. class Outer
2. {
3. int a = 10;
4. private int b = 20;
5. static int access$000(Outer outer)
6. {
7. return outer.b;
8. }
9. void method()
10. {
11. Outer$Local1 local = new Outer$Local1(this);
12. }
13. }
14. class Outer$Local1
15. {
16. Outer$Local1(Outer outer)
17. {
18. this$0 = outer;
19. }
20. void display()
21. {
22. System.out.println(this$0.a);
Code after Compilation
As you can see that the local class has become a regular class again with an
ugly name Outer$Local1. The compiler has also created a constructor for the local
class and added a reference of the Outer class as an argument to it. Remeber you
cannot use any other object than this to create an instance of the local class. For
example the following code will not compile-
Code
1. class Outer
2. {
3. void method()
4. {
5. class Local
6. {
7. }
8. Outer outer = new Outer();
9. Local local = outer.new Local();
10. }
11. }
Arrays of References
This article is about the compile time checks and runtime exceptions related to
Arrays. It will give you a precise list of rules about when the compiler will
reject your code, when JVM will throw an Exception and when will the code
run normally.
In our hierarchy, you can create an array of type Car, and assign it an array of
type Car or Porsche or Lamborghini or their sub-types. So here is a list of valid
and invalid array assignments-
The statements above tell which codes are valid and which are not along with
why they are valid/invalid.
Each element in the array can be assigned a seperate value. The assignment is
done like this-
The rules regarding the values that can be assigned to an array element for
successful compilation and successful execution are as below
carArr[0] = carObj;
The above code will both compile and run successfully as it follows all the
rules.
porscheArr[0] = porscheObj;
The above code will both compile and run successfully. This is because type of
porscheObj(i.e. Porsche) is assignable to type of elements of porscheArr(i.e.
Porsche). The actual type of both porscheObj and elements of porscheArr are
also assignable.
porscheArr[0] = porscheObj;
porscheArr[0] = carObj;
The above compilation will not succed as the type of carObj(i.e. Car) is not
same type or sub-type of type of elements of porscheArr(i.e. Porsche).
porscheArr[0] = lamborghiniObj;
carArr[3] = carObj;
You can assign value to an element in the array using a cast like this-
When you use a Cast for assigning a value to an element in the array using a
Cast these rules are applied.
porscheArr[0] = (Porsche)carObj;
The above code will compile and run successfully. The case is necessary as type
of carObj(i.e. Car) is not a sub-type or same type as the type of each element
of porscheArr(i.e. Porsche). The code will run successfully as the type of the
Cast(i.e. Porsche) and the type of object stored in carObj(i.e. Boxter) are
compatible and the type of elements of porscheArr(i.e. Porsche) and the type
of object stored in carObj(i.e. Boxter) are compatible.
carArr[0] = (Porsche)porscheObj;
The above code will compile and run successfully. However the cast is not
neccesary.
porscheArr[0] = (Porsche)carObj;
The above code will compile successfully. The cast is also necessary. But it will
throw a ClassCastException at run time as the actual object stored in
carObj(i.e. Lamborghini) is incompatible with the cast type(i.e. Porsche).
porscheArr[0] = (Porsche)carObj;
The above code will compile successfully. The cast is also necessary. But it will
throw an ArrayStoreException at run time as the actual object stored in
carObj(i.e. Cayman) is incompatible with the actual type of elements of
Porsche array(i.e. Boxter).
OR
ArrayType[] arrayName = new ActualArrayType[size];
The above code will not compile successfully. Although you can see that the
code will run successfully, even then it will not compile as type of
porscheArr(i.e. Porsche) is not same type or sub type of type of boxterArr(i.e.
Boxter).
You can assign an array to another array reference and then assign a new value
to an element of the Array as follows
newArrayName = anObject;
carArr = lamborghiniObj;
This concludes this topic. If you have any doubts then please leave a comment
and I will clear your doubts.
Posted by Ankit Garg at 12:10 AM 1 comments
Labels: Problem ragarding assignment in Arrays, reason for ArrayStoreException, Rules governing assignment of Array elements
Subscribe to: Posts (Atom)
Blog Archive
▼ 2008 (4)
o ▼ September (4)
Static Inner Classes
Non-Static Inner Classes
Local Classes in Java
Rules for Arrays
Know about Me
Ankit Garg
SCJP 6, 98%
View my complete profile
That's Me