Sunteți pe pagina 1din 30

Me = new Genius("Java");

Monday, September 8, 2008

Static Inner Classes

Static Member Classes


In this post I will tell you everything about Static Member Classes from inside out. You must have been frustrated by remembering about the rules governing Inner Classes. In this post I will tell you how Inner Classes actually work. This will help you get a deep knowledge of Inner Classes which will help you a lot. This code will tell how the compiler changes the Inner Classes. But remember, that is a some difference in the code given here and what is generated by the compiler. This article uses a simplified approach by removing and modifying some of the code generated by the compiler. Static Member Classes can also be called as Static Inner Classes. To begin with let me tell you in brief about Static Member Classes.

They can be declared inside top-level classes or other Static Member Classes These are declared within other classes with the static modifier. They can declare static and non-static members. All accessibility modifiers can be used with these classes. To create an object of Static Member Class, you don't need an instance of its enclosing class. An instance of enclosing class is not stored in an instance of a Static Member Class. They can access static members of the enclosing class including private static members. They can access non-static members of the enclosing class(including private non-static members) on an object of the enclosing class. The reverse is also correct i.e. members of the enclosing class can access static members of the Member Class directly and non-static members of the Member Class on an Object of the Member Class.

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. }

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 thisCode
1. class Outer 2. { 3. static int a = 10;

Code
4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. } private static int b = 20; static class Inner { String a; String b; void display() { System.out.println(Outer.a); System.out.println(Outer.b); } }

//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);

//displays 10

Code
11. 12. 13. 14. } System.out.println(outer.b); } } //displays 20

There is no problem in the Statement # 10 as it accesses a non-private member of the enclosing class. There is nothing wrong with it. But Statement # 11 tries to access a private member of the enclosing class. This is how it is made possible after compilationCode 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. } 10. class Outer$Inner 11. { 12. void display() 13. { 14. Outer outer = new Outer(); 15. System.out.println(outer.a); //displays 10 16. System.out.println(Outer.access$000(outer)); //displays 20 17. } 18. }

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 membersCode
1. class Outer 2. { 3. int a = 10; 4. static int b = 20; 5. int getProduct() 6. { 7. return a * b; 8. }

Code
9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 200 24. 25. 100 26. } static int getCube() { 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. }

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 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. {

Code after Compilation


22. void display() 23. { 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 20. Inner in = new Inner(); 21. System.out.println(in.nonPrivateVal); //displays 22. System.out.println(in.privateVal); //displays 23. in.nonPrivateDisplay(); //displays 24. Private Display! 25. in.privateDisplay(); //displays 26. Private Display! } }

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. {

Code after Compilation


void display() 3. { 4. System.out.println(Inner.staticVal); 5. Outer.Inner in = new Outer.Inner(); 6. System.out.println(in.nonPrivateVal); 7. System.out.println(Outer.Inner.access$000(in)); 8. in.nonPrivateDisplay(); 9. Non-Private Display! 10. Outer.Inner.access$100(in); 11. Private Display! 12. } 13. } 14. class Outer$Inner 15. { 16. static int staticVal = 10; 17. int nonPrivateVal = 20; 18. private int privateVal = 30; 19. void nonPrivateDisplay() 20. { 21. System.out.println("Non-Private Display!"); 22. } 23. private void privateDisplay() 24. { 25. System.out.println("Private Display!"); 26. } 27. static int access$000(Outer$Inner inner) 28. { 29. return inner.privateVal; 30. } 31. static void access$100(Outer$Inner inner) 32. { 33. inner.privateDisplay(); 34. } } //displays 10 //displays 20 //displays 30 //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. { 8. System.out.println("Hello!");

Code
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 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 Inner Classes

Non-Static Member Classes


Lets again start with the introduction of Non-Static Member Classes.

They can be declared inside top-level classes or other Member Classes. They can declare only non-static members and static constants. All accessibility modifiers can be used with these classes. To create an object of Non-Static Member Class, you need an instance of its enclosing class. An instance of enclosing class is stored in an instance of a Non-Static Member Class. They can access all the members of the enclosing class including private members.

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 thisCode
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 thisCode 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. } 10. class Outer$Inner 11. { 12. Outer$Inner(Outer outer) 13. { 14. this$0 = outer; 15. } 16. void display() 17. { 18. System.out.println(this$0.a); 19. System.out.println(Outer.access$000(this$0)); //System.out.println(b); 20. } 21. final Outer this$0; 22. }

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 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 in Java

Local Classes

Static Local Classes


As always, lets begin with an introduction of Static Local Classes

These are classes declared inside static methods. They can declare non-static members and static constants. No accessibility modifiers can be used with these classes. To create an object of Static Local Class, you don't need an instance of its enclosing class. An instance of enclosing class is not stored in an instance of a Static Local Class. They can access static members of the enclosing class including private static members. They can access non-static members of the enclosing class(including private non-static members) on an object of the enclosing class. They can access non-hidden final local variables i.e. variables of the enclosing method including final parameters passed to the enclosing method. They must be declared before use. An object of a Static Local Class can only be created inside the enclosing method as it is local to that method. All the constants declared in a method which are used in the local class must be assigned a value before the declaration of the local class.

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

Code
2. { 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. } static void containerMethod(final int a) { final int b = 10; class Local { void display() { System.out.println(a); System.out.println(b); } } Local local = new Local(); }

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

The declaration at Statement # 8 hides the local declaration of a at Statement # 5. The variable a declared at Statement # 5 cannot be accessed from the methods of the methods of class Local by any means. As you must have read that a Static Local Class must be declared before use. This means that it cannot be specified as the return type of the method in which it is declared. Then is the Static Local Class only created for the enclosing method. Well it can be used if you specify the super class of the Static Local Class as the return type of the method. But then also you can only call method declared in the super class. So you can override the methods of the super class and provide new implementation for those methods. As you know that methods are called on the actual object stored in a reference so the code in methods declared by you will be called. See the example below to understand how it worksCode
1. class Base 2. { 3. public void display() 4. { 5. System.out.println(&qt;Base..&qt;); 6. } 7. } 8. class Outer 9. { 10. static Base containerMethod() 11. { 12. class Local extends Base 13. {

Code
14. public void display() 15. { 16. System.out.println("Local.."); 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. }

The method containerMethod declared at Statement # 10 contains a class Local which extends the class Base declared at Statement # 1. Class Local overrides the method display of the class Base. The return type of containerMethod is Base so it can return an object of type Local(as it is the sub-class of Base). The main method recieves an object of class Local into a reference of class Base. When it calls the method display on that object, the implementation of display method at Statement # 14 is called. The Statement # 32 is not valid as the declared type of reference variable base is Base and method hello is not declared in class Base.

Non-Static Local Classes


Again lets begin with an introduction of Non-Static Local Classes

These are classes declared inside non-static methods. They can declare non-static members and static constants. No accessibility modifiers can be used with these classes. To create an object of Non-Static Local Class, you need an instance of its enclosing class. An instance of enclosing class is stored in an instance of a Non-Static Local Class. They can access all members of the enclosing class including private members.

They can access non-static members of the enclosing class(including private non-static members) on an object of the enclosing class. They can access non-hidden final local variables i.e. variables of the enclosing method including final parameters passed to the enclosing method. They must be declared before use. An object of a Non-Static Local Class can only be created inside the enclosing method as it is local to that method.

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. {

Code after Compilation


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); 23. System.out.println(Outer.access$000(this$0)); 24. } 25. final Outer this$0; 26. }

//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. }

This code will generate this errorOuter.java:9: cannot find symbol symbol : class Local location : class Outer Local local = outer.new Local(); ^ 1 error
Posted by Ankit Garg at 1:46 AM 0 comments Labels: Changes made to Inner Classes by the Compiler, Inner Classes in Java, Java Inner Classes after Compilation, Local Classes, Method Local Classes, NonStatic Local Classes, Static Local Classes

Rules for Arrays

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.

First Scenario - Declaring and Intializing an Array


When you declare and intialize an array, it looks like thisArrayType[] arrayName = new ActualArrayType[size];

In this code you will have to remember thise rule-

must be a sub-type or same type as ArrayType for compilation to succed.


ActualArrayType

This rule is simple. ActualArrayType must be assignable to ArrayType for

to be assigned to ArrayType[]. But this rule is only applicable to reference type arrays. Primitive Array types of assignable primitive types are not assignable to each other. This means that int[] is not assignable to long[] while int is not assignable to long.
ActualArrayType[]

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 assignmentsCar[] carArr = new Car[3]; //Valid, Same-type Car[] carArr = new Lamborghini[3]; //Valid, Sub-type Car[] carArr = new Bozter[3]; //Valid, Indirect Sub-type Porsche[] porscheArr = new Cayman[3]; //Valid, Sub-type Porsche[] porscheArr = new Car[3]; //Invalid, Super-type Porsche[] porscheArr = new Lamborghini[3]; //Invalid, Sibling

The statements above tell which codes are valid and which are not along with why they are valid/invalid.

Second Scenario - Assigning value to an Element in the Array


Each element in the array can be assigned a seperate value. The assignment is done like thisObjectType anObject = new ActualObjectType(); ArrayType[] arrayName = new ActualArrayType[size]; arrayName[indexValue] = anObject;

The rules regarding the values that can be assigned to an array element for successful compilation and successful execution are as below

must be a sub-type or same type as ArrayType for compilation to succed. ActualObjectType must be a sub-type or same type as ActualArrayType(Note: there is no [] here) for successful execution. Otherwise an ArrayStoreException will be thrown
ObjectType

And as everyone knows indexValue must be less than size of array and greater than or equal to 0 otherwise there will be an ArrayIndexOutOfBoundsException on run time.

Here is a list of valid and invalid array assignmentsCar[] carArr = new Car[3]; Car carObj = new Car(); carArr[0] = carObj;

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 code will compile successfully. This is because type of porscheObj(i.e. Porsche) is assignable to type of porscheArr(i.e. Porsche). But this code will generate an ArrayStoreException as the actual type of porscheObj(i.e. Boxter) is not assignable to actual type of porscheArr(i.e. Cayman).
Porsche[] porscheArr = new Porsche[3]; Car carObj = new Porsche(); 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).

Porsche[] porscheArr = new Porsche[3]; Lamborghini lamborghiniObj = new Lamborghini(); porscheArr[0] = lamborghiniObj;

The above compilation will not succed as the type of lamborghiniObj(i.e. Lamborghini) is not same type or sub-type of elements of porscheArr(i.e. Porsche). And last but not the least
Car[] carArr = new Car[3]; Car carObj = new Car(); carArr[3] = carObj;

The above code will compile but will throw an ArrayIndexOutOfBoundsException as the index used in the assignment is not less than the size of the array.

Third Scenario - Assigning value to an Element in the Array using a Type Cast
You can assign value to an element in the array using a cast like thisObjectType anObject = new ActualObjectType(); ArrayType[] arrayName = new ActualArrayType[size]; arrayName[indexValue] = (CastType)anObject;

When you use a Cast for assigning a value to an element in the array using a Cast these rules are applied.

must be a super-type or sub-type or same type as ObjectType for compilation to succed. CastType must be same type or sub-type of ArrayType for compilation to succed. If ObjectType is same type or sub-type of ArrayType then the cast is not neccesary.
CastType

must be a sub-type or same type as CastType for successful execution. Otherwise an ClassCastException will be thrown ActualObjectType must be a sub-type or same type as ActualArrayType(Note: there is no [] here) for successful execution. Otherwise an ArrayStoreException will be thrown Again as everyone knows indexValue must be less than size of array and greater than or equal to 0 otherwise there will be an ArrayIndexOutOfBoundsException on run time.
ActualObjectType

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).

Fourth Scenario - Assigning an Array to another


This can be done as followsArrayType[] arrayName = new ActualArrayType[size]; NewArrayType[] newArrayName = arrayName; OR ArrayType[] arrayName = new ActualArrayType[size]; NewArrayType[] newArrayName = (CastType[])arrayName;

The rules regarding this type of assignment are

must be a super-type or same type as ArrayType for compilation to succed. CastType must be same type or sub-type of ArrayType for compilation to succed. If CastType is same type or sub type as ActualArrayType then ClassCastException is thrown.
NewArrayType

If you are wondering that what would happen if ActualArrayType and NewArrayType are not compatible at run time then what will happen, then there is a good news for you. This scenario can never occur. So don't worry about it. Let's see a few Examples to clear this thingPorsche[] porscheArr = new Boxter[3]; Boxter[] boxterArr = porscheArr;

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;

The above code will compile and run successfully.


Porsche[] porscheArr = new Cayman[3]; Boxter[] boxterArr = (Boxter[])porscheArr;

The above code will compile successfully. But it will throw a ClassCastException at run time as the actual array stored in porscheArr(i.e. Cayman) is inconvertible to type of cast(i.e. Boxter).

Fifth Scenario - Using the new Array reference to change an element of the Array
This scenario is similar to the Second Scenario but I am including it here for those who might think it as a seperate scenario You can assign an array to another array reference and then assign a new value to an element of the Array as follows
ArrayType[] arrayName = new ActualArrayType[size]; NewArrayType[] newArrayName = arrayName; ObjectType anObject = new ActualObjectType(); newArrayName = anObject;

The rules regarding this scenario are

must be a super-type or same type as NewArrayType for compilation to succed. ActualObjectType must be same type or sub-type of ActualArrayType for the program to run successfully.
ObjectType

Let's see our hierarchy in action againPorsche[] porscheArr = new Porsche[3]; Lamborghini lamborghiniObj = new Lamborghini(); Car[] carArr = porscheArr; carArr = lamborghiniObj;

The above code will compile successfully as type of lamborghini(i.e. Lamborghini) is assignable to corresponding non-array type carArr(i.e. Car which is non-array type of Car[]). But it will throw an ArrayStoreException at run type as type of lamborghiniObj(i.e. Lamborghini) is not assignable to actual type of elements stored in carArr(i.e. Porsche).

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

Ya! I look Great!

That's Me

S-ar putea să vă placă și