你好!我們來談談另一種類型的巢狀類別。即關於局部類別(Method local inside class)。在學習之前你需要記住的第一件事是它們在嵌套類別結構中的位置。 根據我們的圖,我們可以理解局部類別是內部類別的子類型,我們在先前的資料中詳細討論過。然而,本地類別與內部類別有許多重要的特徵和區別。關鍵在於它們的聲明:本地類別僅在程式碼區塊中聲明。最常見的是 - 在外部類別的某些方法內。例如,它可能看起來像這樣:
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
有公共方法right
和left
(不是 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
)。這就是他們,本地班級!正如你所看到的,它們與內部類別有很多不同。我們甚至必須深入研究該語言版本的功能才能了解它們是如何運作的:)在下一講中,我們將討論匿名內部類別 - 最後一組嵌套類別。祝你學業順利!:)
GO TO FULL VERSION