Inner Classes

From Suhrid.net Wiki

Jump to: navigation, search

Contents

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.
  • In general inner classes cannot have static members (variables and methods) except for static inner classes.

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) Exception is it can have final static variables (essentially constants).
  • 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();
	}
 
}

Extending Inner Classes

  • Inner classes can be extended, but the overriding class needs to provide the overriden inner class an instance of its outer-class.
  • This is done by providing a mandatory constructor that calls Outer class super constructor. See below.
class OuterA {
 
	class Inner {
 
		int secret = 42;
	}
}
 
class OuterB extends OuterA.Inner {
 
	/*
	 * Mandatory constructor
	 */
	OuterB(OuterA outerARef) {
		outerARef.super();
	}
 
	void printSecret() {
		System.out.println(secret);
	}
}
 
public class InnerInherit {
 
	public static void main(String[] args) {
		OuterB b = new OuterB(new OuterA());
		b.printSecret();
	}
 
}

Local Inner Classes

  • A local class is an inner class that is defined in a block.
  • Block could mean - method body, local block, static initializer, instance initializer.
  • Local class is treated like a method local variable - it can not be marked as public, private, protected, static, transient.
  • Local classes CANNOT have static members - variables or methods.
  • Scope - its scope is limited to the block.
  • 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 ?
  • 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 )

Another Explanation:

  • 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

  • Anonymous classes combine the process of class definition and instantiation.
  • Perhaps the most popular of the inner classes. Used to extend classes or implement interfaces. Convenient for defining classes on the spot. Used a lot in Swing, Threads.
  • Since anonymous class is actually always a subclass - only methods that are in the reference type can be called.
  • It is anonymous - because a new type is not declared using the class keyword and the classes do not have a name. An instance can be created only alongwith class definition.
  • Similar to local classes - no static keywords permitted anywhere.
  • 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"
}
  • Passing an argument to the super-class constructor
class A {
 
	private String s;
 
	A(String s) {
		this.s = s;
	}
 
	void foo() {
		System.out.println("foo()");
	}
 
	String getS() {
		return s;
	}
}
 
public class Anon2 {
 
	public static void main(String[] args) {
		A a1 = new A("Hello") { //Passing the arguement to anon class's super constructor
			@Override
			void foo() {
				System.out.println("foo() " + getS());
			}
		};
 
		a1.foo(); //prints "foo() Hello"
	}
 
}
  • Like method local inner classes, for anonymous classes to access local variables defined in the outer class, the local variables must be final.
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

  • Can comprise the same declarations as those allowed in top-level classes.
  • Treated as a static member of the outer class.
  • Therefore, it cannot access non-static member variables or methods of the outer class.
  • Can be accessed without an enclosing member of the outer class - since it is static.
  • Of course, since it is an inner class - it has access to private static variables and methods of the enclosing 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.
Personal tools