非静态内部类作用:
- 最基本的作用:名字隐藏和组织代码
- 用例:内部类不访问外部类的元素时可以直接new。(bad style!)
- 用例:通过外部类的非静态方法返回内部类的引用,这样隐含了内部类对象和其对应的外部类对象的关系(需要先创建外部类对象,然后通过外部类对象创建内部类对象)。
- 注意:内部类访问内部类的元素时,直接new内部类编译会报错
No enclosing(外围的) instance of type xxx is accessible
- 链接到外部类:访问其外围类所有成员的访问权
- 实现机制:普通内部类(非静态)对象和外部类对象之间存在联系。创建内部类对象时,该对象保存指向其外部类对象的引用。
- 用例:"迭代器"设计模式
非静态内部类使用:
-
外部类类名 + .this
- 用法:返回内部类对象引用的外部类对象
-
外部类对象 + .new
- 用法:返回内部类的引用,告知其它对象或静态方法中使用
// Creating an inner class directory using the .new syntax public class DotNew { public class Inner {} public static void main(String[] args) { DotNew dotNew = new DotNew(); DotNew.Inner inner = dotNew.new Inner(); } }
-
权限修饰符
- 私有内部类,隐藏内部类信息。接口的实现不可见也不可用,仅能得到指向基类或者接口的引用。
- 保护内部类,自己、子类、同一个包中的类可以访问
// 接口所有成员自动是public的 interface Destination { String readLabel(); } interface Contents { int value(); } class Parcel4 { // 私有内部类,隐藏内部类信息。接口的实现不可见也不可用,仅能得到指向基类或者接口的引用。 private class PContents implements Contents { private int i = 11; public int value() { return i; } } // 保护内部类,自己、子类、同一个包中的类可以访问 protected class PDestination implements Destination { private String label; private PDestination(String whereTo) { this.label = whereTo; } public String readLabel() { return this.label; } } public Destination destination(String s) { PDestination pDestination = new PDestination(s); // Exercise 8: 外部类是否可以访问内部类的private元素 // 答:外部类访问内部类的private对象就像访问自己的private对象一样 // 理由:编译器对内部类实现时,生成两个类,对于private变量的访问,编译器会生成一个accessor函数. System.out.println(pDestination.label); return pDestination; } public Contents contents() { return new PContents(); } } public class TestParcel { public static void main(String[] args) { Parcel4 p = new Parcel4(); Contents c = p.contents(); Destination d = p.destination("Tasmania"); // The type Parcel4.PContents is not visible // 不能向下转型成private内部类,因为名字不可访问 // ! Parcel4.PContents pc = p.new PContents(); } }
- 注意:嵌套类(内部静态类)不需要对外部对象的引用
// Exercise 8: 外部类是否可以访问内部类的private元素 // 答:可以,外部类访问内部类的private对象就像访问自己的private对象一样 // 实现机制:编译器对内部类实现时,生成两个类,对于private变量的访问,编译器会生成一个 accessor函数.
方法和作用域内的内部类
-
目的
- 实现了某种类型的接口,可以创建并返回对其的引用
- 创建一个类以解决问题,但不希望这个类是公共可用的
-
局部内部类
- 在方法内部定义类
- 局部内部类可以嵌入某个作用域内(如if作用域),则在作用域外不可访问
-
匿名内部类
- 创建继承/实现自公共类/接口的匿名类的对象
// Returning an instance of an anonymous inner class. // 普通类被当作“公共”接口来使用 class Wrapping { private int i; public Wrapping(int x) { i = x; } public int value() { return i; } } public class Parcel7 { public Contents contents() { // 创建一个实现/继承自Contents的匿名类的对象,且使用默认的构造器 return new Contents() { private int i = 11; public int value() { return i; } }; } public Wrapping wrapping(int x) { // Base constructor call: return new Wrapping(x) { // Pass constructor argument public int value() { return super.value() * 47; } }; } public static void main(String[] args) { Parcel7 parcel7 = new Parcel7(); Wrapping wrapping = parcel7.wrapping(4); System.out.println(wrapping.value()); } }
public class Parcel9 { // Argument must be final to use inside // anonymous inner class: public Destination destination(final String dest) { return new Destination(){ private String label = dest; @Override public String readLabel() { return label; } }; } public static void main(String[] args) { Parcel9 p = new Parcel9(); Destination d = p.destination("Tasmania"); System.out.println(d.readLabel()); } }
- 匿名内部类、局部内部类:若要使用一个在其外部定义的对象,那么编译器会要求其参数引用是final的
Java编译器实现的只是capture-by-value,并没有实现capture-by-reference
interface AnnoInner(){addXYZ();} public class Outer { public AnnoInner getAnnoInner(final int x) { final int y = 100; return new AnnoInner(){ // 编译器相当于拷贝外部自由变量的副本到匿名类 // int copyX=x; // int copyY=y; int z = 100; public int addXYZ() { return x + y + z; } } } }
- 利用实例初始化,以达到为匿名内部类创建一个构造器的效果
- 注意:匿名内部类既可以扩展类,也可以实现接口,但不能同时扩展类和实现接口,且如果是实现接口,也只能实现一个接口
嵌套类
不需要内部类对象与其外部类对象之间有联系。
- 意义
- 要创建嵌套类的对象,并不需要其外围类的对象
- 不能从嵌套类的对象中访问非静态的外围类对象
- 嵌套类可包含static数据和字段,但普通内部类不行