Java的内部类可以获得其外围类的所有成员访问权限,可以很方便的对外围类进行操作。非static内部类都含有一个隐藏的外围类引用,这个引用可以通过 OuterClass.this 来获得使用。通常如果我们的一组操作需要遵循某些规范(例如接口,抽象类),就可以把这一组操作封装成一个内部类来供外部使用,而又不会脱离外围类。参见如下代码:
public interface Selector<E> { boolean hasNext(); E next(); } public class Sequence { private int[] seq; private Selector<Integer> obverseSelector; // 正序遍历器 private Selector<Integer> reverseSelector; // 逆序遍历器 public Sequence(int[] seq) { this.seq = seq; obverseSelector = new Selector<Integer>() { int counter = -1; @Override public Integer next() { return Sequence.this.seq[counter]; } @Override public boolean hasNext() { return (++counter < Sequence.this.seq.length); } }; reverseSelector = new Selector<Integer>() { int counter = Sequence.this.seq.length; @Override public Integer next() { return Sequence.this.seq[counter]; } @Override public boolean hasNext() { return (--counter >= 0); } }; } public Selector<Integer> getObverseSelector() { return obverseSelector; } public Selector<Integer> getReverseSelector() { return reverseSelector; } } public class Test { public static void main(String args[]) { int[] a = {1, 2, 3, 4, 5}; Sequence sequence = new Sequence(a); Selector<Integer> obSelector = sequence.getObverseSelector(); Selector<Integer> reSelector = sequence.getReverseSelector(); while (obSelector.hasNext()) { System.out.print(obSelector.next() + " "); } System.out.println(); while (reSelector.hasNext()) { System.out.print(reSelector.next() + " "); } } }
这个例子通过内部类实现Selector接口,让Sequence类拥有属于自己的正序遍历器和逆序遍历器。注意匿名内部类如果需要访问外围类的成员,需要使用 Outer.this 前缀来访问。
可以想象如果不用内部类来实现遍历器会有什么不同,如果我们不用内部类来实现,就必须定义个Selector的具体类,并且将Sequence类的seq数组作为参数传进去。如果说需要将Selector作为一个共用的遍历器,可以这么做,但如果遍历器是针对Sequence类来实现的,则用内部类更为方便。
下面的代码是使用非匿名内部类实现的Selector
public class Sequence2 { private int[] seq; public Sequence2(int[] seq) { this.seq = seq; } public class ObverseSelecter implements Selector<Integer> { int counter = -1; @Override public Integer next() { return seq[counter]; } @Override public boolean hasNext() { return (++counter < seq.length); } } public class ReverseSelecter implements Selector<Integer> { int counter = seq.length; @Override public Integer next() { return seq[counter]; } @Override public boolean hasNext() { return (--counter >= 0); } } } public class Test { public static void main(String args[]) { int[] a = {1, 2, 3, 4, 5}; Sequence2 sequence2 = new Sequence2(a); Selector<Integer> obSelector2 = sequence2.new ObverseSelecter(); Selector<Integer> reSelector2 = sequence2.new ReverseSelecter(); while (obSelector2.hasNext()) { System.out.print(obSelector2.next() + " "); } System.out.println(); while (reSelector2.hasNext()) { System.out.print(reSelector2.next() + " "); } } }
此处仅需要注意一下生成遍历的时候的new语法 sequence2.new ,容易看出内部类的实例其实是和外围类的实例紧密相关的,正如匿名内部类的 Outer.this 语法一样
值得一提的是局部内部类如果要使用局部变量,那么局部变量必须为final,否则编译会不通过,参见如下代码
public interface Flyable { void fly(); } public class FinalTest { public static Flyable getFlyable(int speed) { final int finalSpeed = speed * 2; return new Flyable() { @Override public void fly() { System.out.println("I can fly with speed " + finalSpeed + " km/h"); } }; } public static void main(String[] args) { Flyable flyable = getFlyable(50); flyable.fly(); } }
可见finalSpeed为final变量,为什么有这种要求呢?
是因为局部变量的生命周期仅仅在方法执行作用域内,当方法执行完毕,局部变量自然就被销毁。但是内部类却作为一个实例被返回来了,如果说这个内部类实例以后还需要访问这个已销毁的局部变量,显然是不可以的。所以在内部类里需要访问的局部变量都会定义成常量,一起被编译进内部类里。