CodeGym /Java Blog /ランダム /ローカルメソッドの内部クラス
John Squirrels
レベル 41
San Francisco

ローカルメソッドの内部クラス

ランダム グループに公開済み
やあ!別の種類のネストされたクラスについて話しましょう。私はローカル クラス (メソッドローカル内部クラス) について話しています。本題に入る前に、まず入れ子になったクラスの構造におけるそれらの位置を覚えておく必要があります。 この図から、ローカル クラスは内部クラスの亜種であることがわかります。これについては、前の資料ローカル メソッドの内部クラス - 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;
           }
       }

       // ...number validation code
   }
}
重要!Java 7 がインストールされている場合、このコードを IDEA に貼り付けるとコンパイルされません。その理由についてはレッスンの最後で説明します。つまり、ローカル クラスがどのように機能するかは、言語のバージョンに大きく依存します。このコードがコンパイルできない場合は、IDEA の言語バージョンを Java 8 に切り替えるか、final次のように単語をメソッド パラメーターに追加しますvalidatePhoneNumber(final String number)。その後はすべてうまくいきます。これは電話番号を検証する小さなプログラムです。このvalidatePhoneNumber()メソッドは文字列を入力として受け取り、それが電話番号であるかどうかを判断します。そして、このメソッド内でローカルPhoneNumberクラスを宣言しました。当然のことながら、その理由を尋ねるかもしれません。メソッド内でクラスを宣言するのはなぜでしょうか? なぜ通常の内部クラスを使用しないのでしょうか? 確かに、私たちは、PhoneNumberクラスは内部クラスです。ただし、最終的な解決策はプログラムの構造と目的によって異なります。内部クラスに関するレッスンの例を思い出してみましょう。

public class Bicycle {

   private String model;
   private int maxWeight;

   public Bicycle(String model, int maxWeight) {
       this.model = model;
       this.maxWeight = maxWeight;
   }
  
   public void start() {
       System.out.println("Let's go!");
   }

   public class HandleBar {

       public void right() {
           System.out.println("Steer right!");
       }

       public void left() {

           System.out.println("Steer left!");
       }
   }
}
その中で、HandleBarバイクの内部クラスを作成しました。違いは何ですか? まず、クラスの使用方法が異なります。2 番目の例のクラスは、最初の例のクラスHandleBarよりも複雑なエンティティです。PhoneNumberまず、HandleBarpublicメソッドrightleftメソッドがあります (これらはセッター/ゲッターではありません)。第二に、それとその外部クラスがどこで必要になるかを事前に予測することは不可能ですBicycle。1 つのプログラムでも、数十の異なる場所と方法が存在する可能性があります。しかしPhoneNumber、クラスを使用すると、すべてがはるかに簡単になります。私たちのプログラムはとてもシンプルです。その目的は 1 つだけです。それは、番号が有効な電話番号であるかどうかを確認することです。ほとんどの場合、私たちのPhoneNumberValidatorスタンドアロン プログラムですらなく、より大きなプログラムの承認ロジックの一部になります。たとえば、さまざまな Web サイトでは、ユーザーがサインアップするときに電話番号の入力を求めることがよくあります。数字の代わりに意味のないものを入力すると、Web サイトは「これは電話番号ではありません!」というエラーを報告します。このような Web サイト (またはそのユーザー認証メカニズム) の開発者は、次のようなものを組み込むことができます。PhoneNumberValidator彼らのコードでは。言い換えれば、1 つのメソッドを持つ 1 つの外部クラスがあり、プログラム内の 1 つの場所で使用され、他の場所では使用されません。そして、それが使用された場合、その中では何も変わりません。1 つのメソッドがその役割を果たし、それだけです。この場合、すべてのロジックが 1 つのメソッドに集められるため、そこに追加のクラスをカプセル化する方がはるかに便利で適切です。ゲッターとセッター以外に独自のメソッドはありません。実際、必要なのはコンストラクターからのデータのみです。他のメソッドには関与しません。したがって、それが使用される唯一のメソッドの外でそれに関する情報を取得する理由はありません。また、ローカル クラスがメソッド内で宣言される例も示しましたが、これが唯一のオプションではありません。コードブロック内で簡単に宣言できます。

public class PhoneNumberValidator {
  
   {
       class PhoneNumber {

           private String phoneNumber;

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

   }

   public void validatePhoneNumber(String phoneNumber) {

      
       // ...number validation 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;
               }
           }
          
           // ...some logic
       }

       // ...number validation 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;
           }
       }

       // ...number validation code
   }

   // Error! The compiler does not recognize the PhoneNumber class
   public PhoneNumber generatePhoneNumber() {

   }

}
ローカル クラスのもう 1 つの重要な機能は、ローカル変数およびメソッド パラメーターにアクセスできることです。忘れた方のために付け加えておきますが、メソッド内で宣言された変数は「ローカル」変数と呼ばれます。つまり、何らかの理由でメソッドString usCountryCode内にローカル変数を作成するとvalidatePhoneNumber()、ローカル クラスからその変数にアクセスできるようになりますPhoneNumber。ただし、プログラムで使用される言語のバージョンに応じて微妙な点が多くあります。レッスンの冒頭で、サンプルの 1 つのコードが Java 7 でコンパイルできない可能性があることに注意しました。覚えていますか? この理由を考えてみましょう :) Java 7 では、ローカル クラスは、finalメソッド内で次のように宣言されている場合にのみ、ローカル変数またはメソッド パラメータにアクセスできます。

public void validatePhoneNumber(String number) {

   String usCountryCode = "+1";

   class PhoneNumber {

       private String phoneNumber;

       // Error! The method parameter must be declared as final!
       public PhoneNumber() {
           this.phoneNumber = number;
       }

       public void printUsCountryCode() {

           // Error! The local variable must be declared as final!
           System.out.println(usCountryCode);
       }

   }

   // ...number validation code
}
ここでコンパイラは 2 つのエラーを生成します。そしてここではすべてが順調です:

public void validatePhoneNumber(final String number) {

   final String usCountryCode = "+1";

    class PhoneNumber {

       private String phoneNumber;

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

       public void printUsCountryCode() {

           System.out.println(usCountryCode);
       }

    }

   // ...number validation code
}
これで、レッスンの最初のコードがコンパイルされない理由がわかりました。Java 7 では、ローカル クラスはfinalメソッド パラメーターとfinalローカル変数にのみアクセスできます。Java 8 では、ローカル クラスの動作が変更されました。このバージョンの言語では、ローカル クラスはfinalローカル変数とパラメータだけでなく、.html の変数やパラメータにもアクセスできますeffective-finalEffective-final初期化以来値が変更されていない変数です。たとえば、Java 8 では、usCountryCode変数がそうでない場合でも、コンソールに変数を簡単に表示できますfinal。重要なのは、その価値が変わらないということです。次の例では、すべてが正常に動作します。

public void validatePhoneNumber(String number) {

  String usCountryCode = "+1";

    class PhoneNumber {

       public void printUsCountryCode() {

           // Java 7 would produce an error here
           System.out.println(usCountryCode);
       }

    }

   // ...number validation code
}
ただし、初期化の直後に変数の値を変更すると、コードはコンパイルされません。

public void validatePhoneNumber(String number) {

  String usCountryCode = "+1";
  usCountryCode = "+8";

    class PhoneNumber {

       public void printUsCountryCode() {

           // Error!
           System.out.println(usCountryCode);
       }

    }

   // ...number validation code
}
ローカル クラスが内部クラスの概念の亜種であるのも不思議ではありません。それらには共通の特徴もあります。 ローカル クラスは、外部クラスのすべての (プライベートを含む) フィールドとメソッド (静的および非静的の両方) にアクセスできます。 たとえば、静的String phoneNumberRegexフィールドをバリデーター クラスに追加してみましょう。

public class PhoneNumberValidator {

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

   public void validatePhoneNumber(String phoneNumber) {
       class PhoneNumber {
          
           // ......
       }
   }
}
検証はこの静的変数を使用して実行されます。このメソッドは、渡された文字列に正規表現 " " に一致しない文字[^0-9](つまり、0 から 9 までの数字ではない文字) が含まれているかどうかを確認します。この変数にはローカルPhoneNumberクラスから簡単にアクセスできます。たとえば、ゲッターを作成します。

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;
           }
       }

       // ...number validation 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;
           }
       }

       // ...number validation code
   }
}
静的初期化子 (初期化ブロック) またはインターフェイスはローカル クラスで宣言できません。ただし、ローカル クラスは、定数変数 ( ) であれば、静的メンバーを持つことができますstatic final。これで、ローカル クラスについておわかりいただけたでしょう。ご覧のとおり、これらは通常の内部クラスとは多くの違いがあります。どのように機能するかを理解するために、言語の特定のバージョンの機能をさらに詳しく調べる必要がありました :) 次のレッスンでは、匿名内部クラス (入れ子になったクラスの最後のグループ) について説明します。勉強頑張ってください!:)
コメント
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION