JavaRush /Java Blog /Random-KO /로컬 메서드의 내부 클래스

로컬 메서드의 내부 클래스

Random-KO 그룹에 게시되었습니다
안녕하세요! 또 다른 유형의 중첩 클래스에 대해 이야기해 보겠습니다. 즉, 로컬 클래스(메서드 로컬 내부 클래스)에 관한 것입니다. 공부하기 전에 가장 먼저 기억해야 할 것은 중첩 클래스 구조에서의 위치입니다. 다이어그램을 기반으로 로컬 클래스가 이전 자료 중 하나로컬 메소드의 내부 클래스 - 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첫째, y HandleBar에는 공개 메소드 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-local 변수 및 매개변수뿐만 아니라 에도 액세스할 수 있습니다 effective-final. Effective-final초기화 이후 값이 변경되지 않은 변수입니다. russianCountryCode예를 들어, Java 8에서는 변수 가 아니더라도 콘솔에 변수를 쉽게 표시할 수 있습니다 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