GIST NOTES 8 - Java Inner Classes
[DISCLAIMER: This is solely for non-commercial use. I don't claim ownership of this content. This is a crux of all my readings studies and analysis. Some of them are excerpts from famous books on the subject. Some of them are my contemplation upon experiments with direct hand coded code samples using IDE or notepad.
I've created this mainly to reduce an entire book into few pages of critical content that we should never forget. Even after years, you don't need to read the entire book again to get back its philosophy. I hope these notes will help you to replay the entire book in your mind once again.]
[JDK 7]
>Inner classes have powerful and efficient uses in specific situations
>produce code easier to read and maintain
>over use of inner classes lead to complex code leading to a syndrome known as "reuseless": code that's useless over and over again
>inner classes allow to define classes within classes
>type of scoping for inner classes is provided when we make one class as a member of another class
>static inner class is called "top level nested class" is not really an inner class, but a class in itself defined within the scope of another class
>when you need a separate class for a job and still need it to be intimately connected to your main class, we go for inner classes; e.g. event handlers; they are one of the main reasons inner classes were added to the language
>the intimacy is needed because, event handlers for example need to access text content on the GUI component directly when user events occur; that means event handlers need access to the members of the GUI classes
>especially the situation gets even clear to separate event handler from main classes is when both of them need to extend from different classes/parents(multiple inheritance not possible); so we are better off not combining those two classes
>it even gets clearer when the event handler needs to access the private members of the main class(making those members public and exposing is not a good OO design and will lead to problems)
>this is where main class and inner class create a special relationship with each other; this relationship is shared between one inner class intance and one instance of outer class, as if inner class is part of outer class(it really is)
>inner class can access everything that is of outer class(this is not a violation of encapsulation because, whoever writes outer class also writes the inner class and they share the same class space; and the privileges given to inner class is not available to anyone else in the OO world)
>four types of inner classes: 1.inner classes(regular) 2.static 3. method-local 4.anonymous
Inner classes
-------------
>we define it within the curly braces of the outer class
class Outer{
class Inner{}
}
>since inner class is a separate class, compiler generates a separate file for it (Outer$Inner.class)
>but directly running the inner class from command prompt would require main method in Inner class, and no static keyword of anykind is allowed inside the Inner class definition because it is tied to a live instance of the Outer class. Hence, no one can run the Inner class
>when you decompile Outer$Inner.class you get something like the following:-
class Outer$Inner
{
final Outer this$0;
Outer$Inner()
{
this$0 = Outer.this;
super();
}
}
#don't be fooled by 'this$0'; it is just a regular identifier; point is, compiler puts a final variable holding an instance of Outer class inside the Inner class
>only way to access an inner class is through a live instance of outer class; inner class doesn't exist until an outer class instance gets created in the JVM
>just like an instance method can access the private variables, inner classes also can access the private variables of the outer class; inner class is a valid member of(instance member as well) outer class
Instantiating Inner Class
-------------------------
To create an instance of inner class, you must have an instance of the outer class to tie to the inner class. There is no exception to this rule. An inner class instance can never stand alone without a direct relationship to an instance of the outer class
>"new Inner()" is considered (instantiating inner class) as a non-static variable and cannot be performed within static context; for example, the main method of outer class cannot instantiate Inner class, or any other static method or static block for that matter; only constructors or instance methods of Outer class can instantiate Inner class
class Outer{
void create(){
Inner in = new Inner(); //legal
}
public static void main(String[] args){
Inner in1 = new Inner(); //illegal
Inner in2 = new Outer().new Inner(); //legal - but not possible outside Outer class
}
}
>considering the above rule, there is a way to instantiate Inner within a static context or outside of Outer class
Outer outer = new Outer();
Outer.Inner a1 = outer.new Inner();
Outer.Inner a2 = outer.(new Inner()); //illegal
Outer.Inner a3 = new Outer().new Inner();
Outer.Inner a4 = new Outer().(new Inner()); //illegal
Outer.Inner a5 = (new Outer()).(new Inner()); //illegal
Outer.Inner a6 = (outer).new Inner();
Outer.Inner a7 = (new Outer()).new Inner();
#don't bracket the Inner constructor call; new operator should follow the dot
Within Inner
------------
>to refer Inner instance use 'this'
>to refer Outer instance to whom inner instance is tied to, use 'Outer.this'
Modifiers applicable to Inner class declaration (as a member of Outer class)
----------------------------------------------------------------------------
final
abstract
public
private
protected
static (Inner is no longer an inner class; it becomes nested class)
strictfp
Method Local Inner Classes
==========================
>inner class defined within a method; instantiated like a regular class within the same method but only after its definition
>it can only access final variables of the method(variables declared final within the method)
>it can access all members of Outer class
>it can instantiate other inner classes of Outer class directly
>it can refer itself with 'this' and Outer class with 'Outer.this' but cannot refer any other non-enclosing inner classes of Outer with 'this' keyword
>method-local inner class can again nested within another method local inner class
>modifiers applicable to method-local-inner class declarations: final, abstract and strictfp
>public, private, protected, transient and static modifiers are not allowed
>same rule goes for all nested inner classes of method-local-inner-class; classes within method-local-inner-class can have only final, abstract or strictfp modifiers (this is just like for any variable within a method)
>inner classes within method-local-inner classes can have again inner classes within their methods and so on to any number of levels
>inner classes within method-local-inner classes can have regular inner classes as well
>unless you are crazy, you can stop at one method-local-inner class :-)
>method local inner classes can only be instantiated and used within that method itself and not anywhere anywhere at all
>method local inner classes cannot access method local variables(except for final variables), because, those variables only live in stack; their lifetime is only during the execution of the method on the stack; but method-local-inner class instance can still stick around in the heap even after the completion of the said method; at that state method local variables no longer exist but inner class object is still functioning; so this rule;
>on the side note, volatile is applicable for instance variables(of all types of classes and inner classes) only; cannot be used for method local variables
>suppose if you put your inner class inside a static method, what happens: this inner class can access only static members of the enclosing class; cannot use 'this' operator; no access to any instance members; same restriction as for static methods applies to this inner class as well
Anonymous Inner Classes
-----------------------
>We love them; they come handy when you are coding swing GUI application and need quick event listeners
>anonymous inner classes have no name; they can be defined anywhere you can run any statement(constructors, blocks, methods); even inside method call argument list
class Outer {
private Parent p = new Parent() {
public void displayName() {
System.out.println(this.getClass().getName());
}
public void myOwnMethod(){}
};
}
#inner class was created in the assignment statement of the instance variable of Outer class
#inner class has no name, but extends from Parent class and overrides displayName() method
#variable p holds an instance of this anonymous subclass of Parent class
#calling p.displayName() will run the overridden version of the method;
#it displays the random name given by the compiler to our anonymous inner class(something like Outer$1)
#anonymous class definition must end with semicolon to keep the assignment statement intact
>since the anonymous instance is held in the parent class reference variable, we cannot call methods specific to the anonymous instance
>though the anonymous class is free to define its own methods and use them inside, no one can access them and there is no means to reach them by anyone because nobody knows the name of the new class
>in the above example, we can't say p.myOwnMethod()
>we can make an anonymous inner class to implement an interface instead of extending/subclassing a class
Runnable newGuy = new Runnable() {
public void run(){}
};
#here, we know that Runnable is an interface, and we created a concrete runnable class which has no name;
#this looks like instantiating an interface, but it is not
#and we can implement only one interface like this; no way an anonymous inner class can implement multiple interfaces
#or extending one class and implementing one interface at the same time is also not possible
>basically usage of 'implements' or 'extends' keywords is forbidden for anonymous inner classes
>inner classes within method argument list
public static void processThread( new Runnable(){ public void run(){} } );//is a single statement
Static Nested Classes
----------------------
>these are referred to as 'static inner classes'
>these are really not inner classes according to the standard definition of inner classes
>while a real inner class has a special relationship with outer class instance, a static inner class or static nested class does not have that relationship with outer class instance
>with static nested classes, it is really about namespace resolution than about an implicit relationship between two classes
>it is just a top-level or regular class scoped within another class
>it is also a static member of the enclosing class
class Outer {
static class Nested {}
}
>Nested itself isn't really static; there is no such thing as static classes; it just means that Nested will be a static member of Outer and can be accessed like any other static members; that is without the need for any instances of Outer class to access it
>static nested class cannot access any instance members of the enclosing class, but can access other static members
>it can be instantiated anywhere(in the enclosing class or a thirdparty class) as follows
File 1:
class Outer {
private static String name = "doggy";
private String desc = "";
static class Inner {
public void display() {
System.out.println(name); //legal
System.out.println(desc); //illegal
System.out.println(this); //legal - Inner is still a class of its own
System.out.println(Outer.this); //illegal
new Inner(); //legal
}
}
private void hi() {
Inner in = new Inner(); //legal
}
public static void main(String[] args) {
Inner in = new Inner(); //legal
}
static{ Inner in = new Inner(); } //legal static block
{ Inner in = new Inner(); } //legal non-static block
}
File 2:
class ThirdParty {
hello() {
Outer.Inner in = new Outer.Inner(); // legal - one 'new' and a pair of parentheses are missing
//from the syntax of regular inner class instantiation
Inner in = new Inner(); //illegal
}
}
Reference: Kathy Sierra's SCJP Book
No comments:
Post a Comment