Professional Documents
Culture Documents
Me New Genius ("Java") Static Member Classes
Me New Genius ("Java") Static Member Classes
Genius("Java");
Monday, September 8, 2008
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);
9.
}
10.
}
11. }
//displays 10
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 thisCode 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);
10.
}
11. }
//displays
10
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);
9.
}
10.
}
11. }
//displays 10
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 happensCode 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. }
Code
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15. }
//displays 10
//displays 20
The compiler generated code will look like thisCode after Compilation
1. class Outer
2. {
3.
static int a = 10;
4.
private static int b = 20;
5.
static int access$000()
6.
{
7.
return b;
8.
}
9. }
10. class Outer$Inner
11. {
12.
String a;
13.
String b;
14.
void display()
15.
{
16.
System.out.println(Outer.a);
//displays 10
17.
System.out.println(Outer.access$000()); //displays 20
18.
}
19. }
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);
11.
System.out.println(outer.b);
//displays 10
//displays 20
Code
12.
13.
14. }
}
}
Code
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23. 200
24.
25. 100
26.
}
{
return b * b * b;
}
static class Inner
{
void display()
{
b = 2;
System.out.println(b);
System.out.println(getCube());
Outer outer = new Outer();
outer.a = 100;
System.out.println(outer.getProduct());
System.out.println(outer.a);
//displays 2
//displays 8
//displays
//displays
}
}
There is not much changes made in this code by the compiler. So the code after
compilation would look asCode after Compilation
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.
{
11.
return b * b * b;
12.
}
13. }
14. class Outer$Inner
15. {
16.
void display()
17.
{
18.
Outer.b = 2;
19.
System.out.println(Outer.b);
20.
System.out.println(Outer.getCube());
21.
Outer outer = new Outer();
22.
outer.a = 100;
23.
System.out.println(outer.getProduct());
24.
System.out.println(outer.a);
25.
}
26. }
//displays 2
//displays 8
//displays 200
//displays 100
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 classCode
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 belowCode 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. }
original statements are shown in comments. Now lets see how you can call
private methods of the enclosing classCode
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));
16.
Outer outer = new Outer();
17.
outer.getProduct(10,20);
18.
}
19.
}
20. }
//displays 9
//displays 200
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 belowCode 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.
{
10
20
30
Non-
The compiler will generate package visible methods for the access statements
at Statements # 22 and 24. So the code would look like thisCode after Compilation
1. class Outer
2. {
3.
void display()
4.
{
//displays
10
//displays
//displays
//displays
//displays
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 ExampleCode
1. class Outer
2. {
3.
static int a = 10;
4.
static class Inner
5.
{
6.
void display()
7.
{
Code
8.
9.
10.
11. }
System.out.println("Hello!");
}
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 thisCode 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.
The reverse is also correct i.e. members of the enclosing class can
access static(constants) members of the Member Class directly and
non-static members of the Member Class on an Object of the Member
Class.
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 thisCode
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 programCode
1. class Outer
2. {
3.
class Inner
4.
{
5.
}
6.
public static void main(String[] args)
7.
{
8.
Inner in = new Inner();
9.
}
10. }
If you compile this program you will get this errorOuter.java:8: non-static variable this cannot be referenced from a
static context
Inner obj = new Inner();
^
1 error
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 aboveCode after Compilation
1. class Outer
2. {
3.
public static void main(String[] args)
4.
{
5.
Outer out = new Outer();
6.
Outer.Inner in = new Outer.Inner(out);
7.
}
8. }
9. class Outer$Inner
10. {
11.
Outer$Inner(Outer outer)
12.
{
13.
this$0 = outer;
14.
}
15.
final Outer this$0;
16. }
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 belowCode
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()
Code
8.
9.
10.
11.
12.
13. }
{
System.out.println(a);
System.out.println(Outer.this.a);
}
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 belowCode after Compilation
1. class Outer
2. {
3.
int a = 10;
4. }
5. class Outer$Inner
6. {
7.
int a = 100;
8.
Outer$Inner(Outer outer)
9.
{
10.
this$0 = outer;
11.
}
12.
void display()
13.
{
14.
System.out.println(a);
15.
System.out.println(this$0.a); //System.out.println(Outer.this.a);
16.
}
17.
final Outer this$0;
18. }
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.
How a Static Local Class access the static and non-static
members of the enclosing class is similar to what you have seen in Static
Member Class. We must directly jump to how the Static Local Class accesses
the final local variables. Well the concept is just simple. When you create a
local class the compiler adds the final local variables as a paremeter to the
local class constructor and stores them in corresponding final variables that
the compiler adds to the local class. Lets see this phenomenonCode
1. class Outer
2. {
3.
static void containerMethod(final int a)
4.
{
Code
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16. }
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 codeCode 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 ExampleCode
1. class Outer
2. {
3.
static void containerMethod()
4.
{
5.
final int a = 10;
6.
class Local
7.
{
8.
int a = 100;
9.
void display()
10.
{
11.
System.out.println(a);
12.
}
13.
}
14.
Local local = new Local();
15.
}
16. }
//hides local a
//displays 100
Code
17.
}
18.
public void hello()
19.
{
20.
System.out.println("Hello!!");
21.
}
22.
}
23.
return new Local();
24.
}
25. }
26. class MainClass
27. {
28.
public static void main(String[] args)
29.
{
30.
Base base = Outer.containerMethod();
31.
base.display();
//displays Local..
32.
//base.hello();
//not a member of base
33.
}
34. }
How a Local Class access the final local variables, static and non-static
members of the enclosing class is similar to what you have seen in Static Local
Class. We will be jumping directly to how they get an enclosing instance. Well
this is similar to Non-Static Member Classes. The compiler adds the this
reference wherever we create an instance of the local class in the method.
Code
1. class Outer
2. {
3.
int a = 10;
4.
private int b = 20;
5.
void method()
6.
{
7.
class Local
8.
{
9.
void display()
10.
{
11.
System.out.println(a);
12.
System.out.println(b);
13.
}
14.
}
15.
Local local = new Local();
16.
}
17. }
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)
{
this$0 = outer;
}
void display()
{
System.out.println(this$0.a);
System.out.println(Outer.access$000(this$0));
}
final Outer this$0;
//System.out.println(b);
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 compileCode
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.
We will use this inheritance hierarchy as a reference in this article.
The statements above tell which codes are valid and which are not along with
why they are valid/invalid.
The rules regarding the values that can be assigned to an array element for
successful compilation and successful execution are as below
Here is a list of valid and invalid array assignmentsCar[] carArr = new Car[3];
The above code will both compile and run successfully as it follows all the
rules.
Porsche[] porscheArr = new Boxter[3];
Porsche porscheObj = new Boxter();
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.
Porsche[] porscheArr = new Cayman[3];
Porsche porscheObj = new Boxter();
porscheArr[0] = porscheObj;
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).
Porsche[] porscheArr = new Porsche[3];
Lamborghini lamborghiniObj = new Lamborghini();
porscheArr[0] = lamborghiniObj;
When you use a Cast for assigning a value to an element in the array using a
Cast these rules are applied.
Now let's see these rules in our hierarchyPorsche[] porscheArr = new Porsche[3];
Car carObj = new Boxter();
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.
Car[] carArr = new Porsche[3];
Porsche porscheObj = new Porsche();
carArr[0] = (Porsche)porscheObj;
The above code will compile and run successfully. However the cast is not
neccesary.
Porsche[] porscheArr = new Porsche[3];
Car carObj = new Lamborghini();
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).
Porsche[] porscheArr = new Boxter[3];
Car carObj = new Cayman();
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).
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).
Porsche[] porscheArr = new Boxter[3];
Boxter[] boxterArr = (Boxter[])porscheArr;
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
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