Inner Classes

From Suhrid.net Wiki
Jump to navigationJump to search

Introduction

  • Inner classes have access to members (variables and methods - including private) of the outside classes.
  • This is one of the biggest advantages of inner classes !
  • Think of the inner class as sort of a MEMBER of the outer class.
  • So access modifiers can be used (public, private etc) for inner class declarations.

Regular inner classes

  • A regular class is defined as a member of the outer class. (analogous to a non-static field).
  • An inner class is tied to an outer-class instance - so it can access instance specific information.
  • This implies that regular Inner classes cannot have any statics (variables or methods)
  • Only way to access an instance of the inner classes is through an instance of the outer class.
  • Remember the funny syntax:
class Outer {
  class Inner() {

  }
}

Outer o = new Outer();
Outer.Inner i =  o.new Inner()
  • There are different ways on how regular inner class instances can be created, depending on where it is accessed from. See below:
class Outer {
	private String s = "";
	
	Outer() {
		
	}
	
	Outer(String s) {
		this.s = s;
	}
	
	public String toString() {
		return s;
	}
	
	class Inner {
		private String s = "";
		
		Inner() {
			
		}
		
		Inner(String s) {
			this.s  = s;
		}
		
		public String toString() {
			return s;
		}
		
		void in() {
			System.out.println("Inner object : " + this);       //The inner objects instance
			System.out.println("Outer object : " + Outer.this); //Access the outer object's instance
		}
		
		void makeInner() {
			Inner i = new Inner(); //Within the inner class, so regular way to access.
		}
	}
	
	private void makeInner() {
		Outer o = new Outer();        //Accessing from within the outer class
		Inner i = o. new Inner();     //Class name can be used as-is
	}
	
}

public class TestInner {
	
	public static void main(String[] args) {
		Outer o1 = new Outer("o1");              //Accessing from outside the outerclass
		Outer.Inner o1i1 = o1.new Inner("o1i1"); //Class name has to be Outer.Inner 
		o1i1.in();
	}
	
}

Method Local Inner Classes

  • Class defined inside a method. Like a method local variable - its scope is limited to the method.
  • It is treated like a method local variable - it cant be marked as public, private, protected, static, transient.
  • Class can ONLY be instantiated from within the method where it is declared. That too, after the class definition. Not anywhere else.
  • Like other inner classes they can access the private variables of the outer class.
  • The class cannot use the local variables of the method, including the method arguments unless the variables are declared as final.
  • Why ? The class object can continue to live on the heap, till it's GC'ed even after the method is popped off from the stack, so trying to accessing method local variables after they are destroyed will be disastrous.
  • By making the variables final, this is in effect turning them into constants. So the compiler can replace the variables with actual values, thus eliminating the problem of the inner class accessing destroyed variables.
private void foo(final int x) {
		
		String str = "hello";
		
		class InnerFoo {
			public void go() {
				System.out.println("x is : " + x); //This is OK, because x is marked as final.
				// System.out.println(str); //This won't work because str is not final.
			}
		}
		
}

Anonymous Inner Classes

  • Perhaps the most popular of the inner classes.
  • Used to extend classes or implement interfaces.
  • It is anonymous - because a new type is not declared using the class keyword.
  • Convenient for defining classes on the spot. Used a lot in Swing, Threads.
  • See example below. We need to create a new thread just in a method. Instead of defining a separate thread class, implementing runnable etc. We can do the following:
public void foo() {
               
                Thread t1 = new Thread(new Runnable() {
                        
                        void go() {

                        }			

			public void run() {
				System.out.println("HerpDerp");
			}
		});
		
		t1.start();
                
                //Note: t1.go() will not work. Since methods can be invoked based on the reference type only. i.e. what is defined in Thread.
                //Even shorter

                new Thread(new Runnable() {
			public void run() {
				System.out.println("HerpDerp");
			}
		}).start();
               
}
  • One more example:
interface Chewable {
	void chew();
}

class Gum implements Chewable {

	@Override
	public void chew() {
		System.out.println("Chew chew");
	}
}

public class AnonInner {

	public static void main(String[] args) {
		
                //Define a new type on the spot
		Chewable c = new Chewable() {
			public void chew() {
				System.out.println("inner chew");
			}
		};
		
		go(c);
		
		//OR
		
                //Within the argument
		go(new Chewable() {
			public void chew() {
				System.out.println("chew-c");
			}
		});
	}
	
	private static void go(Chewable c) {
		c.chew();
	}
}
  • You can create an anonymous class overriding the same outerclass in which it was defined.
class AnonInner {
                 
                String anon() {
                    return "anoninner";
                }

                System.out.println(new AnonInner() {
			
		}.anon()); 
                //Anonymously extending the same class without defining new behavior. This will print "anoninner"
		
		System.out.println(new AnonInner() {
			String anon() {
				return "override";
			}
		}.anon());
                //Anonymously extending the same class while overriding a method. This will print "override"
}
  • For anonymous classes to access local variables defined in the outer class, the local variables must be final.
  • This is like method local inner classes.
  • WHY ?
  • Because an inner class possesses the implicit this reference, it can directly access instance variables of its outer class. However, there is no way for an inner class to directly access local variables of its outer class. How should the JVM pass a local variable declared in one class file to another class file?
  • In order to solve this problem, the JVM makes a requirement for the developer to declare the local variable as final.
  • The compiler then places a hidden variable with the name val$count inside the byte code for outclass$anonymousinnerclass.class (in below code, it would be something like : Anon$1.class)
  • The variable val$count is assigned the same value as the final local variable count.
  • Because a variable declaration in one class is replicated in another class, both declarations must be the same i.e. both declarations must reference the same object. The final keyword is used to guarantee this.

( Above explanation is from http://www.coderanch.com/t/543455/java/java/annonymous-classes-final-keyword )

public class Anon {

	public static void main(String[] args) {
		countUntil();
	}
	
	private static void countUntil() {
		
		final int count = 10;
		
		new Thread() {
			public void run() {
				for(int i=1; i <= count ; i++) {
					System.out.print(i + " ");
				}
			}
		}.start();
	}

}

Static Inner Classes

  • Treated as a static member of the outer class
  • It cannot access non-static member variables or methods of the outer class.
  • Can be accessed without an enclosing member of the outer class.
class Souter {
	
	static class Sinner {
		public void repent() {
			
		}
	}
   
}


public class StaticInnerTest {

	public static void main(String[] args) {
		Souter.Sinner sinner = new Souter.Sinner();
		sinner.repent();
	}
	
}
  • If an import is used. then the outer class reference can also be eliminated e.g
import Souter.Sinner;

...
...

Sinner sinner = new Sinner(); //Outer class reference can be avoided.