本地类

原文: https://docs.oracle.com/javase/tutorial/java/javaOO/localclasses.html

本地类是在中定义的类,它是一组平衡括号之间的零个或多个语句。您通常会在方法体中找到定义的本地类。

本节包括以下主题:

您可以在任何块内定义本地类(有关详细信息,请参阅表达式,语句和块)。例如,您可以在方法体,for循环或if子句中定义本地类。

以下示例 LocalClassExample 验证两个电话号码。它定义方法validatePhoneNumber中的本地类PhoneNumber

  1. public class LocalClassExample {
  2. static String regularExpression = "[^0-9]";
  3. public static void validatePhoneNumber(
  4. String phoneNumber1, String phoneNumber2) {
  5. final int numberLength = 10;
  6. // Valid in JDK 8 and later:
  7. // int numberLength = 10;
  8. class PhoneNumber {
  9. String formattedPhoneNumber = null;
  10. PhoneNumber(String phoneNumber){
  11. // numberLength = 7;
  12. String currentNumber = phoneNumber.replaceAll(
  13. regularExpression, "");
  14. if (currentNumber.length() == numberLength)
  15. formattedPhoneNumber = currentNumber;
  16. else
  17. formattedPhoneNumber = null;
  18. }
  19. public String getNumber() {
  20. return formattedPhoneNumber;
  21. }
  22. // Valid in JDK 8 and later:
  23. // public void printOriginalNumbers() {
  24. // System.out.println("Original numbers are " + phoneNumber1 +
  25. // " and " + phoneNumber2);
  26. // }
  27. }
  28. PhoneNumber myNumber1 = new PhoneNumber(phoneNumber1);
  29. PhoneNumber myNumber2 = new PhoneNumber(phoneNumber2);
  30. // Valid in JDK 8 and later:
  31. // myNumber1.printOriginalNumbers();
  32. if (myNumber1.getNumber() == null)
  33. System.out.println("First number is invalid");
  34. else
  35. System.out.println("First number is " + myNumber1.getNumber());
  36. if (myNumber2.getNumber() == null)
  37. System.out.println("Second number is invalid");
  38. else
  39. System.out.println("Second number is " + myNumber2.getNumber());
  40. }
  41. public static void main(String... args) {
  42. validatePhoneNumber("123-456-7890", "456-7890");
  43. }
  44. }

该示例通过首先从电话号码中删除除 0 到 9 之外的所有字符来验证电话号码。之后,它检查电话号码是否包含正好十位数(北美电话号码的长度)。此示例打印以下内容:

  1. First number is 1234567890
  2. Second number is invalid

本地类可以访问其封闭类的成员。在前面的示例中,PhoneNumber构造器访问成员LocalClassExample.regularExpression

此外,本地类可以访问局部变量。但是,本地类只能访问声明为 final 的局部变量。当本地类访问封闭块的局部变量或参数时,它捕获该变量或参数。例如,PhoneNumber构造器可以访问局部变量numberLength,因为它被声明为 final; numberLength捕获变量

但是,从 Java SE 8 开始,本地类可以访问最终的封闭块的局部变量和参数,或者有效地终结。在初始化之后其值永远不会改变的变量或参数实际上是最终的。例如,假设变量numberLength未声明为 final,并在PhoneNumber构造器中添加突出显示的赋值语句,以将有效电话号码的长度更改为 7 位数:

  1. PhoneNumber(String phoneNumber) {
  2. numberLength = 7;
  3. String currentNumber = phoneNumber.replaceAll(
  4. regularExpression, "");
  5. if (currentNumber.length() == numberLength)
  6. formattedPhoneNumber = currentNumber;
  7. else
  8. formattedPhoneNumber = null;
  9. }

由于这个赋值语句,变量numberLength不再是有效的最终结果。因此,Java 编译器生成类似于“从内部类引用的局部变量必须是最终的或有效最终的”的错误消息,其中内部类PhoneNumber尝试访问numberLength变量:

  1. if (currentNumber.length() == numberLength)

从 Java SE 8 开始,如果在方法中声明本地类,它可以访问方法的参数。例如,您可以在PhoneNumber本地类中定义以下方法:

  1. public void printOriginalNumbers() {
  2. System.out.println("Original numbers are " + phoneNumber1 +
  3. " and " + phoneNumber2);
  4. }

方法printOriginalNumbers访问方法validatePhoneNumber的参数phoneNumber1phoneNumber2

封闭范围中具有相同名称的本地类影子声明中的类型(例如变量)的声明。有关详细信息,请参阅 Shadowing

本地类与内部类类似,因为它们无法定义或声明任何静态成员。静态方法中的本地类,例如在静态方法validatePhoneNumber中定义的类PhoneNumber,只能引用封闭类的静态成员。例如,如果未将成员变量regularExpression定义为静态,则 Java 编译器会生成类似于“非静态变量regularExpression无法从静态上下文引用的错误”。

本地类是非静态的,因为它们可以访问封闭块的实例成员。因此,它们不能包含大多数类型的静态声明。

你不能在一个块内声明一个接口;接口本质上是静态的。例如,以下代码摘录无法编译,因为接口HelloThere是在方法体greetInEnglish中定义的:

  1. public void greetInEnglish() {
  2. interface HelloThere {
  3. public void greet();
  4. }
  5. class EnglishHelloThere implements HelloThere {
  6. public void greet() {
  7. System.out.println("Hello " + name);
  8. }
  9. }
  10. HelloThere myGreeting = new EnglishHelloThere();
  11. myGreeting.greet();
  12. }

您不能在本地类中声明静态初始化程序或成员接口。以下代码摘录无法编译,因为方法EnglishGoodbye.sayGoodbye声明为static。当遇到此方法定义时,编译器会生成类似于“修饰符’static’的错误,只允许在常量变量声明中使用”:

  1. public void sayGoodbyeInEnglish() {
  2. class EnglishGoodbye {
  3. public static void sayGoodbye() {
  4. System.out.println("Bye bye");
  5. }
  6. }
  7. EnglishGoodbye.sayGoodbye();
  8. }

本地类可以具有静态成员,前提是它们是常量变量。 (常量变量是一个原始类型或类型String的变量,它被声明为 final 并用编译时常量表达式初始化。编译时常量表达式通常是一个字符串或一个算术表达式,可以在编译时进行评估。有关更多信息,请参阅了解类成员。)以下代码摘录编译,因为静态成员EnglishGoodbye.farewell是一个常量变量:

  1. public void sayGoodbyeInEnglish() {
  2. class EnglishGoodbye {
  3. public static final String farewell = "Bye bye";
  4. public void sayGoodbye() {
  5. System.out.println(farewell);
  6. }
  7. }
  8. EnglishGoodbye myEnglishGoodbye = new EnglishGoodbye();
  9. myEnglishGoodbye.sayGoodbye();
  10. }