内部类

2016-02-25 15:47:09   最后更新: 2016-02-25 15:47:09   访问数量:542




一言以蔽之,内部类就是将一个类的定义放在另一个类的定义内部

内部类在平常的编码过程中应用的场景并不多,本文主要来总结一下内部类的特性和用法

 

下面的例子展示了内部类组织代码和名字隐藏的功能:

package com.techlog.test; /** * Created by techlog on 16/2/24. */ public class Parcel2 { private class Destination { private String label; Destination(String whereTo) { label = whereTo; } String readLabel() { return label; } } public Destination to(String s) { return new Destination(s); } public void ship(String dest) { Destination d = to(dest); System.out.println(d.readLabel()); } public static void main(String[] argv) { Parcel2 p = new Parcel2(); p.ship("Tasmania"); } }

 

 

Destination 作为 Parcel2 的一个内部类,通过外部类提供的 to 方法返回了对象实例供外部类的成员方法使用

与类中任何其他成员一样,内部类可以被设置为 private、protected、friendly 或 public,也可以设置为 static,从而把他变为静态内部类,也称为嵌套类

 

共有四种内部类:

  1. 常规内部类
  2. 静态内部类(嵌套类)
  3. 局部内部类
  4. 匿名内部类

 

内部类访问外围类元素

内部类对象可以访问外围类对象所有成员

package com.techlog.test; /** * Created by techlog on 16/2/24. */ interface Selector { boolean end(); Object current(); void next(); } public class Sequence { private Object[] items; private int next = 0; public Sequence(int size) { items = new Object[size]; } public void add(Object x) { if (next < items.length) { items[next++] = x; } } private class SequenceSelector implements Selector { private int i = 0; public boolean end() { return i == items.length; } public Object current() { return items[i]; } public void next() { if (i < items.length) i++; } } public Selector selector() { return new SequenceSelector(); } public static void main(String[] argv) { Sequence sequence = new Sequence(10); for (int i = 0; i < 10; i++) { sequence.add(Integer.toString(i)); } Selector selector = sequence.selector(); while (!selector.end()) { System.out.print(selector.current() + " "); selector.next(); } } }

 

 

上面的代码实现了一个迭代器,可以看到,内部类 SequenceSelector 通过访问 外围类的 private 成员 items 实现了这个迭代器

常规内部类可以访问外围类的任何成员

由于常规内部类只能在实例化后使用,外部不能直接访问常规内部类内的数据,因此常规内部类不能包含 static 字段,也不能包含 static 内部类

 

.this 与 .new

常规内部类中之所以可以调用外部类的任何成员,是因为在内部类实例化的过程中会传入外部类的引用,通过 .this 就可以获取到这个传入的外围类的引用:

同时,通过 .new 可以创建一个对象的常规内部类对象

 

package com.techlog.test; /** * Created by techlog on 16/2/24. */ public class Test { class InnerTest { public Test getOuter() { return Test.this; } } public void printHello() { System.out.println("Test say: hello"); } public static void main(String[] argv) { Test test = new Test(); Test.InnerTest innerTest = test.new InnerTest(); innerTest.getOuter().printHello(); } }

 

 

上面的代码通过外围类 Test 对象 test.new 创建了内部类 InnerTest 的对象,然后在内部类中通过外围类 Test.this 返回了外围类的引用

 

使用 static 修饰的内部类就是静态内部类,和类静态方法一样,外围类对象的引用不会传递给静态内部类,因此静态内部类不能访问外围类的成员与示例方法,只能访问 static 成员和 static 方法

静态内部类与外围类对象之间并没有什么联系,所以也就被称为嵌套类

他有两个特性:

  1. 要创建嵌套类对象,并不需要外围类对象
  2. 不能从嵌套类的对象中访问非静态外围类对象

 

static 内部类作为外围类的嵌套类,只是嵌套于外围类,与普通的类的用法是完全一致的

package com.techlog.test; public class Test { private static String s = "world"; static class StaticInner { void print() { System.out.println("hello " + Test.s); } } public static void main(String[] argv) { Test.StaticInner inner = new Test.StaticInner(); inner.print(); } }

 

上面的代码展示了静态内部类的用法,可以看到,静态内部类的创建和使用与普通的外围类是没有区别的,唯一的区别在于他可以访问外围类的所有 static 成员

 

接口中也可以嵌套类,所有接口中的嵌套类都默认是 public static 的,这样可以通过接口创建公共代码,甚至在嵌套类中实现外围接口的方法

 

在方法体或语句块(包括方法、构造方法、局部块或静态初始化块)内部定义的类称为局部内部类

有以下特性:

  1. 局部内部类只在定义它的方法中有效,类似于方法的局部变量,方法外不能访问,不能使用 private、protected、public 等访问修饰说明符修饰,也不能使用static修饰,但可以使用 final和 abstract 修饰
  2. 非 static 方法中的局部内部类与常规内部类一样,可以访问外围类的所有成员,但是只能访问外围方法中的 final 变量
  3. static 方法中的局部内部类与嵌套类一样,只能访问外围类的 static 成员

 

package com.techlog.test; public class Test { public AbstractTest func() { class LocalInner extends AbstractTest { void say() { System.out.println("hello world"); } } return new LocalInner(); } public static void main(String[] argv) { Test test = new Test(); test.func().say(); } } abstract class AbstractTest { abstract void say(); }

 

 

上面的代码展示了局部内部类的一个常用用法

由于方法返回了一个抽象类对象,因此必须在方法内部创建继承自该抽象类的子类,这个子类这里实现成了局部内部类,在 return 时向上转型为抽象类 AbstractTest

局部内部类不仅可以放在方法内部,也可以放在 if、局部块、静态初始化块等任何作用域内部,它只能在定义它的作用域内部被使用

 

相较于局部内部类,匿名内部类更加常用:

package com.techlog.test; public class Test { public AbstractTest func() { return new AbstractTest() { @Override void say() { System.out.println("hello world"); } }; } public static void main(String[] argv) { Test test = new Test(); test.func().say(); } } abstract class AbstractTest { abstract void say(); }

 

上面的代码展示了匿名内部类的使用,由于内部类只在 return 的时候使用一次,因此它的名字并没有必要存在

匿名内部类通常用来继承一个类或实现一个接口并临时使用用来实例化一个临时的子类,但匿名内部类有下面两个限制:

  1. 不能同时继承一个类并且实现一个接口,或同时实现多个接口
  2. 因为匿名内部类没有类名,因此匿名内部类不能有构造方法

与常规内部类一样,匿名内部类也可以访问外围类的所有成员

 






技术帖      龙潭书斋      class      java      接口      interface      thinking in java      final      java编程思想      static      内部类      implement      匿名内部类      嵌套类      静态内部类     


京ICP备15018585号