在本节中,您将学习嵌套类,在何处以及如何使用它们。
在何处使用嵌套类
顾名思义, 嵌套类 是在一个类中定义的另一个类。这是一个嵌套类:
public class EnclosingClass {
...
public class NestedClass {
...
}
}
与成员变量和方法一样,也可在任何范围内定义 Java 类,包括 public
、private
或 protected
。如果希望以一种面向对象的方式执行类中的内部处理,嵌套类可能很有用,但此功能仅限于需要它的类。
通常,需要一个与定义它的类紧密耦合的类时,可使用嵌套类。嵌套类能够访问包含它的类中的私有数据,但此结构会带来一些负面影响,开始使用嵌套(或内部)类时这些影响并不明显。
嵌套类中的范围
因为嵌套类具有范围,所以它受范围规则的约束。例如,只能通过该类的实例(对象)访问一个成员变量。嵌套类也是如此。
假设在 Manager
和一个名为 DirectReports
的嵌套类之间具有以下关系,后者是一个向 Manager
报告工作情况的 Employee
集合:
public class Manager extends Employee {
private DirectReports directReports;
public Manager() {
this.directReports = new DirectReports();
}
...
private class DirectReports {
...
}
}
就像每个 Manager
对象表示一个唯一的人一样,DirectReports
对象表示一组向经理报告工作情况的真实的人(员工)。在不同的 Manager
之间 DirectReports
是不同的。在本例中,仅在包含 DirectReports
嵌套类的 Manager
实例中引用该类是合理的,所以我将它声明为 private
。
公共嵌套类
因为 DirectReports 是 private
,所以只有 Manager
可创建 DirectReports
实例。但是假设您想为一个外部实体提供创建 DirectReports
实例的能力。在这种情况下,似乎可以为 DirectReports
类提供 public
范围,然后任何外部代码即可创建 DirectReports
实例,如清单 19 所示。
清单 19. 创建 DirectReports
实例:第一次尝试
public class Manager extends Employee {
public Manager() {
}
...
public class DirectReports {
...
}
}
//
public static void main(String[] args) {
Manager.DirectReports dr = new Manager.DirectReports();// This won't work!
}
清单 19 中的代码无法工作,而且您可能知道原因是什么。问题(和它的解决方案)依赖于在 Manager
中定义 DirectReports
的方式,以及范围规则。
再讲讲范围规则
如果有一个 Manager
的成员变量,您会认为编译器会要求您拥有 Manager
对象的引用,然后才能引用它,对吧?DirectReports
也是如此,至少您在清单 19 中定义它时是这样。
要创建一个公共嵌套类的实例,需要使用 new
运算符的一个特殊版本。结合使用对一个外部类的封闭实例的引用,new
允许创建嵌套类的实例:
public class Manager extends Employee {
public Manager() {
}
...
public class DirectReports {
...
}
}
// Meanwhile, in another method somewhere...
public static void main(String[] args) {
Manager manager = new Manager();
Manager.DirectReports dr = manager.new DirectReports();
}
在第 12 行上可以注意到,该语法需要使用对封闭实例的引用,还要加上一个点和 new
关键字,后跟想要创建的类。
静态内部类
有时,您想创建一个(在概念上)与另一个类紧密耦合的类,但范围规则比较宽松,不需要封闭实例的引用。这时就需要静态内部类发挥作用了。一个常见的示例是实现一个 Comparator
,用于比较同一个类的两个实例,通常用于对类进行排序(或分类):
public class Manager extends Employee {
...
public static class ManagerComparator implements Comparator<Manager> {
...
}
}
// Meanwhile, in another method somewhere...
public static void main(String[] args) {
Manager.ManagerComparator mc = new Manager.ManagerComparator();
...
}
在本例中,您不需要封闭实例。静态内部类的行为与它们的对应常规 Java 类类似,而且仅在需要将一个类与它的定义紧密耦合时才使用静态内部类。显然,对于像 ManagerComparator
这样的实用程序类,创建外部类没有必要,而且可能让代码库变的很乱。将这些类定义为静态内部类是一种解决办法。
匿名内部类
使用 Java 语言时,您可在任何地方声明类,如有必要,甚至可在一个方法内声明,而且甚至无需为类提供名称。此功能基本来讲是一种编译器窍门,但有时拥有匿名内部类很方便。
清单 20 构建于清单 17 中的示例之上,添加了一个默认方法来处理不属于 StockOptionEligible
的 Employee
类型。该清单首先显示 HumanResourcesApplication
中一个处理库存选项的方法,然后是一个驱动该方法的 JUnit 测试: