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자전거의 내부 클래스를 만들었습니다. 차이점이 뭐야? 우선 클래스가 사용되는 방식이 다릅니다. 두 번째 예의 클래스 는 첫 번째 예의 클래스 HandleBar보다 더 복잡한 엔터티입니다 . PhoneNumber첫째, HandleBar공개 rightleft메소드(세터/게터가 아님)가 있습니다. 둘째, 우리가 그것을 필요로 할 수 있는 곳과 그것의 외부 클래스를 미리 예측하는 것은 불가능합니다 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) {

      
       // ...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() {

   }

}
로컬 클래스의 또 다른 중요한 기능은 로컬 변수 및 메소드 매개변수에 액세스하는 기능입니다. 잊어버린 경우를 대비해 메서드 내에서 선언된 변수를 "로컬" 변수라고 합니다. 즉, 어떤 이유로 메소드 String usCountryCode내부에 로컬 변수를 생성하면 validatePhoneNumber()로컬 PhoneNumber클래스에서 액세스할 수 있습니다. 그러나 프로그램에서 사용되는 언어 버전에 따라 달라지는 미묘함이 많이 있습니다. 수업 시작 부분에서 예제 중 하나에 대한 코드가 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
}
여기서 컴파일러는 두 가지 오류를 생성합니다. 그리고 여기에 모든 것이 정돈되어 있습니다.

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로컬 변수 및 매개변수뿐만 아니라 effective-final. Effective-final초기화 이후 값이 변경되지 않은 변수입니다. 예를 들어 Java 8에서는 변수 usCountryCodefinal. 중요한 것은 그 가치가 변하지 않는다는 것입니다. 다음 예에서는 모든 것이 정상적으로 작동합니다.

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클래스에서 이 변수에 쉽게 액세스할 수 있습니다. 예를 들어 다음과 같이 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;
           }
       }

       // ...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