こんにちは!別のタイプのネストされたクラスについて話しましょう。つまり、ローカルクラス(メソッドローカル内部クラス)についてです。学習する前に最初に覚えておく必要があるのは、入れ子になったクラスの構造におけるそれらの位置です。 この図に基づいて、ローカル クラスが内部クラスのサブタイプであることが理解できます。これについては、前の資料の 1 つで詳しく説明しました。ただし、ローカル クラスには多くの重要な機能があり、内部クラスとは異なります。鍵はその宣言にあります。ローカル クラスはコード ブロック内でのみ宣言されます。ほとんどの場合、外部クラスのメソッド内で使用されます。たとえば、次のようになります。
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
内部化することもできます。もう 1 つは、最終的な決定はプログラムの構造と目的によって決まるということです。内部クラスに関する講義の例を思い出してください。
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
自転車の内部クラス (ハンドルバー) を作成しました。違いは何ですか? まずはクラスの使い方から。2 番目の例のクラスは、最初の例よりHandleBar
も複雑なエンティティです。PhoneNumber
まず、 yHandleBar
にはパブリック メソッドがありright
、left
(セッターとゲッターではありません)。第 2 に、それとその外部クラスがどこで必要になるかを事前に予測することはできませんBicycle
。これらは、同じプログラム内であっても、数十の異なる場所やメソッドになる可能性があります。しかし、クラスを使用すると、PhoneNumber
すべてがはるかに簡単になります。私たちのプログラムはとてもシンプルです。機能は 1 つだけです。番号が電話番号であるかどうかを確認することです。ほとんどの場合、私たちのPhoneNumberValidator
プログラムは独立したプログラムでさえなく、メイン プログラムの承認ロジックの一部にすぎません。たとえば、さまざまな Web サイトでは、登録時に電話番号の入力を求められることがよくあります。また、数字の代わりに意味のないものを入力すると、サイトには「これは電話番号ではありません!」というエラーが表示されます。このようなサイト (またはむしろユーザー認証メカニズム) を操作するために、その開発者はコードに私たちの類似物を含めることができますPhoneNumberValidator
。言い換えれば、プログラム内の 1 つの場所で使用され、他の場所では使用されない 1 つのメソッドを持つ 1 つの外部クラスがあります。そうなったとしても、その中では何も変わりません。1 つのメソッドがその役割を果たし、それだけです。この場合、すべての作業ロジックが 1 つのメソッドに収集されるため、そこに追加のクラスをカプセル化する方がはるかに便利で正確です。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() {
}
}
ローカル クラスのもう 1 つの重要な機能は、ローカル変数およびメソッド パラメーターにアクセスできることです。忘れた方のために付け加えておきますが、「local」はメソッド内で宣言された変数です。つまり、何らかの目的でString russianCountryCode
メソッド内にローカル変数を作成するとvalidatePhoneNumber()
、ローカル クラスからその変数にアクセスできますPhoneNumber
。ただし、ここにはプログラムで使用されている言語のバージョンに依存する多くの微妙な点があります。講義の冒頭で、例の 1 つのコードは 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 валидации номера
}
ここでコンパイラは 2 つのエラーをスローしました。しかし、ここではすべてが順調です。
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
-local 変数とパラメーターだけでなく、 にもアクセスできます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
。たとえば、ゲッターを作成します。
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