JavaRush /Java 博客 /Random-ZH /本地方法中的内部类

本地方法中的内部类

已在 Random-ZH 群组中发布
你好!我们来谈谈另一种类型的嵌套类。即关于局部类(Method local inside class)。在学习之前你需要记住的第一件事是它们在嵌套类结构中的位置。 本地方法中的内部类 - 2根据我们的图,我们可以理解局部类是内部类的子类型,我们在之前的资料中详细讨论过。然而,本地类与内部类有许多重要的特征和区别。关键在于它们的声明:本地类仅在代码块中声明。最常见的是 - 在外部类的某些方法内。例如,它可能看起来像这样:
public class PhoneNumberValidator {

   public void validatePhoneNumber(String number) {

        class PhoneNumber {

           private String phoneNumber;

           public PhoneNumber() {
               this.phoneNumber = number;
           }

           public String getPhoneNumber() {
               return phoneNumber;
           }

           public void setPhoneNumber(String phoneNumber) {
               this.phoneNumber = phoneNumber;
           }
       }

       //...code валидации номера
   }
}
重要的!如果你安装了Java 7,这段代码在粘贴到IDEA时将无法编译,我们将在讲座的最后讨论这个问题的原因。总而言之,本地类的工作高度依赖于语言版本。如果此代码无法编译,您可以将 IDEA 中的语言版本切换为 Java 8,或者final在方法参数中添加一个单词,使其看起来像这样:validatePhoneNumber(final String number)。在此之后一切都会正常。这是一个小程序——电话号码验证器。它的方法validatePhoneNumber()以字符串作为输入并确定它是否是电话号码。在这个方法中我们声明了我们的本地类PhoneNumber。您可能有一个逻辑问题:为什么?为什么要在方法中声明类?为什么不使用常规内部类?事实上,我们可以这样做:将类设为PhoneNumber内部类。另一件事是,最终决定取决于程序的结构和目的。让我们记住有关内部类的讲座中的示例:
public class Bicycle {

   private String model;
   private int mawWeight;

   public Bicycle(String model, int mawWeight) {
       this.model = model;
       this.mawWeight = mawWeight;
   }

   public void start() {
       System.out.println("Go!");
   }

   public class HandleBar {

       public void right() {
           System.out.println("Steering wheel to the right!");
       }

       public void left() {

           System.out.println("Steering wheel to the left!");
       }
   }
}
在其中我们创建了HandleBar(车把)自行车的内部类。有什么不同?首先,在使用类上。第二个示例中的类是一个比第一个示例中HandleBar更复杂的实体。PhoneNumber首先, yHandleBar有公共方法rightleft(不是 setter 和 getter)。其次,我们无法提前预测Bicycle我们可能在哪里需要它及其外部类 - 这些可能是数十个不同的地方和方法,即使在同一个程序中也是如此。但有了班级,PhoneNumber一切就变得简单多了。我们的程序非常简单。它只有一个功能——检查该号码是否是电话号码。大多数情况下,我们的程序PhoneNumberValidator甚至不会是一个独立的程序,而只是主程序授权逻辑中的一部分。例如,在各种网站上,注册时经常会要求您输入电话号码。如果您输入一些无意义的内容而不是数字,该网站将显示错误:“这不是电话号码!” 对于这样一个站点的操作(或者更确切地说,用户授权机制),其开发人员可以在代码中包含我们的类似内容PhoneNumberValidator。换句话说,我们有一个带有一种方法的外部类,该方法将在程序中的一处使用,而不会在其他地方使用。如果确实如此,那么它不会发生任何改变:一种方法完成它的工作 - 仅此而已。在这种情况下,由于所有工作逻辑都集中在一个方法中,因此在那里封装一个额外的类会更加方便和正确。除了 getter 和 setter 之外,它没有自己的方法。我们本质上只需要其中的构造函数数据。它不用于其他方法。因此,没有理由将有关它的信息扩展到使用它的单一方法之外。我们给出了在方法中声明本地类的示例,但这不是唯一的可能性。它可以简单地在代码块中声明:
public class PhoneNumberValidator {

   {
       class PhoneNumber {

           private String phoneNumber;

           public PhoneNumber(String phoneNumber) {
               this.phoneNumber = phoneNumber;
           }
       }

   }

   public void validatePhoneNumber(String phoneNumber) {


       //...code валидации номера
   }
}
或者甚至是循环for
public class PhoneNumberValidator {


   public void validatePhoneNumber(String phoneNumber) {

       for (int i = 0; i < 10; i++) {

           class PhoneNumber {

               private String phoneNumber;

               public PhoneNumber(String phoneNumber) {
                   this.phoneNumber = phoneNumber;
               }
           }

           //...Howая-то логика
       }

       //...code валидации номера
   }
}
但这种情况极为罕见。在大多数情况下,声明仍然会发生在方法内部。所以,我们处理了公告,我们也谈到了“哲学”:) 本地类与内部类还有什么其他特点和区别? 局部类对象不能在声明它的方法或块之外创建。 假设我们需要一个生成generatePhoneNumber()随机电话号码并返回PhoneNumber. 在当前情况下,我们将无法在验证器类中创建这样的方法:
public class PhoneNumberValidator {

   public void validatePhoneNumber(String number) {

        class PhoneNumber {

           private String phoneNumber;

           public PhoneNumber() {
               this.phoneNumber = number;
           }

           public String getPhoneNumber() {
               return phoneNumber;
           }

           public void setPhoneNumber(String phoneNumber) {
               this.phoneNumber = phoneNumber;
           }
       }

       //...code валидации номера
   }

   //ошибка! компилятор не понимает, что это за класс - PhoneNumber
   public PhoneNumber generatePhoneNumber() {

   }

}
局部类的另一个重要特性是能够访问局部变量和方法参数。如果您忘记了,“local”是在方法内声明的变量。也就是说,如果我们出于某些目的String russianCountryCode在方法内创建局部变量validatePhoneNumber(),则可以从局部类访问它PhoneNumber。然而,这里有很多微妙之处,取决于程序中使用的语言版本。在讲座开始时,我们注意到其中一个示例中的代码可能无法在 Java 7 中编译,还记得吗?现在让我们看看这样做的原因:) 在 Java 7 中,局部类只能访问局部变量或方法参数,如果它们在方法中声明为final
public void validatePhoneNumber(String number) {

   String russianCountryCode = "+7";

   class PhoneNumber {

       private String phoneNumber;

       //ошибка! параметр метода должен быть объявлен How final!
       public PhoneNumber() {
           this.phoneNumber = number;
       }

       public void printRussianCountryCode() {

           //ошибка! локальная переменная должна быть объявлена How final!
           System.out.println(russianCountryCode);
       }

   }

   //...code валидации номера
}
这里编译器抛出了两个错误。但这里一切都井然有序:
public void validatePhoneNumber(final String number) {

   final String russianCountryCode = "+7";

    class PhoneNumber {

       private String phoneNumber;


       public PhoneNumber() {
           this.phoneNumber = number;
       }

       public void printRussianCountryCode() {

           System.out.println(russianCountryCode);
       }

    }

   //...code валидации номера
}
现在您知道讲座开头的代码无法编译的原因了吧:Java 7 中的本地类只能访问final-method 参数和final-local 变量。在 Java 8 中,本地类的行为发生了变化。在此版本的语言中,本地类不仅可以访问final本地变量和参数,还可以访问effective-final. Effective-final是一个变量,其值自初始化以来未曾更改。例如,在 Java 8 中,我们可以轻松地将变量显示到控制台russianCountryCode,即使它不是final。最主要的是它的含义没有改变。在这个例子中,一切都按预期进行:
public void validatePhoneNumber(String number) {

  String russianCountryCode = "+7";

    class PhoneNumber {

       public void printRussianCountryCode() {

           //в Java 7 здесь была бы ошибка
           System.out.println(russianCountryCode);
       }

    }

   //...code валидации номера
}
但是,如果我们在初始化后立即更改变量的值,则代码将无法编译。
public void validatePhoneNumber(String number) {

  String russianCountryCode = "+7";
  russianCountryCode = "+8";

    class PhoneNumber {

       public void printRussianCountryCode() {

           //error!
           System.out.println(russianCountryCode);
       }

    }

   //...code валидации номера
}
但本地类是内部类的子类型也不是没有道理的!他们也有共同点。本地类可以访问外部类的所有(甚至私有)字段和方法:静态和非静态。例如,让我们向验证器类添加一个静态字段String phoneNumberRegex
public class PhoneNumberValidator {

   private static String phoneNumberRegex = "[^0-9]";

   public void validatePhoneNumber(String phoneNumber) {
       class PhoneNumber {

           //......
       }
   }
}
将使用此静态变量执行验证。该方法检查传递给它的字符串是否包含与正则表达式“”不匹配的字符[^0-9](即该字符不是从0到9的数字)。我们可以轻松地从本地类访问这个变量PhoneNumber。例如,编写一个 getter:
public String getPhoneNumberRegex() {

   return phoneNumberRegex;
}
局部类与内部类类似,因为它们不能定义或声明任何静态成员。静态方法中的本地类只能引用封闭类的静态成员。例如,如果您没有将封闭类的变量(字段)定义为静态,则 Java 编译器会生成错误:“无法从静态上下文引用非静态变量。” 局部类不是静态的,因为它们可以访问包含块的实例成员。因此,它们不能包含大多数类型的静态声明。您不能在块内声明接口;接口本质上是静态的。此代码将无法编译:
public class PhoneNumberValidator {
   public static void validatePhoneNumber(String number) {
       interface I {}

       class PhoneNumber implements I{
           private String phoneNumber;

           public PhoneNumber() {
               this.phoneNumber = number;
           }
       }

       //...code валидации номера
   }
}
但是,如果在外部类内部声明了一个接口,则该类PhoneNumber可以实现它:
public class PhoneNumberValidator {
   interface I {}

   public static void validatePhoneNumber(String number) {

       class PhoneNumber implements I{
           private String phoneNumber;

           public PhoneNumber() {
               this.phoneNumber = number;
           }
       }

       //...code валидации номера
   }
}
局部类不能声明静态初始化程序(初始化块)或接口。但是局部类可以有静态成员,只要它们是常量变量 ( static final)。这就是他们,本地班级!正如你所看到的,它们与内部类有很多不同。我们甚至必须深入研究该语言版本的功能才能了解它们是如何工作的:)在下一讲中,我们将讨论匿名内部类 - 最后一组嵌套类。祝你学业顺利!:)
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION