集册 Java 精简入门教程 Java 嵌套类/内部类

Java 嵌套类/内部类

欢马劈雪     最近更新时间:2020-08-21 11:12:27

523

在本节中,您将学习嵌套类,在何处以及如何使用它们。

在何处使用嵌套类

顾名思义, 嵌套类 是在一个类中定义的另一个类。这是一个嵌套类:

public class EnclosingClass {
  ...
  public class NestedClass {
  ...

  }
}

与成员变量和方法一样,也可在任何范围内定义 Java 类,包括 publicprivateprotected。如果希望以一种面向对象的方式执行类中的内部处理,嵌套类可能很有用,但此功能仅限于需要它的类。

通常,需要一个与定义它的类紧密耦合的类时,可使用嵌套类。嵌套类能够访问包含它的类中的私有数据,但此结构会带来一些负面影响,开始使用嵌套(或内部)类时这些影响并不明显。

嵌套类中的范围

因为嵌套类具有范围,所以它受范围规则的约束。例如,只能通过该类的实例(对象)访问一个成员变量。嵌套类也是如此。

假设在 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 中的示例之上,添加了一个默认方法来处理不属于 StockOptionEligibleEmployee 类型。该清单首先显示 HumanResourcesApplication 中一个处理库存选项的方法,然后是一个驱动该方法的 JUnit 测试:

清单 20. 处理不属于 StockOptionEligibleEmployee 类型
展开阅读全文