JavaRush /Java Blog /Random-KO /중첩된 내부 클래스 또는 Java의 내부 클래스

중첩된 내부 클래스 또는 Java의 내부 클래스

Random-KO 그룹에 게시되었습니다
안녕하세요! 오늘 우리는 중요한 주제인 Java에서 중첩 클래스가 작동하는 방식을 살펴보겠습니다. 영어에서는 중첩 클래스(nested class)라고 합니다. Java를 사용하면 다른 클래스 안에 일부 클래스를 만들 수 있습니다.
class OuterClass {
    ...
    static class StaticNestedClass {
        ...
    }
    class InnerClass {
        ...
    }
}
중첩이라고 불리는 것은 이러한 클래스입니다. 그들은 두 가지 유형으로 나뉩니다.
  1. 비정적 중첩 클래스 - 비정적 중첩 클래스입니다. 다른 방식으로 내부 클래스라고도 합니다.
  2. 정적 중첩 클래스 - 정적 중첩 클래스입니다.
내부 클래스에는 두 가지 특별한 하위 유형이 있습니다. 내부 클래스는 단지 내부 클래스일 수 있다는 사실 외에도 다음과 같은 경우도 가능합니다.
  • 현지 수업
  • 익명 클래스
조금 어렵다고요? :) 괜찮습니다. 명확성을 위한 다이어그램은 다음과 같습니다. 강의 중에 갑자기 혼란스러우면 다시 찾아보세요! 중첩된 내부 클래스 - 2오늘 강의에서는 내부 클래스, 즉 내부 클래스(비정적 중첩 클래스, 비정적 중첩 클래스이기도 함)에 대해 이야기하겠습니다. 길을 잃지 않도록 일반 다이어그램에서 특별히 강조 표시되어 있습니다 :) 분명한 질문부터 시작하겠습니다. 이 클래스를 "내부"라고 부르는 이유는 무엇입니까? 대답은 매우 간단합니다. 왜냐하면 다른 클래스 내부에서 생성되기 때문입니다. 예는 다음과 같습니다.
public class Bicycle {

   private String model;
   private int weight;

   public Bicycle(String model, int weight) {
       this.model = model;
       this.weight = weight;
   }

   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!");
       }
   }

   public class Seat {

       public void up() {

           System.out.println("The seat is up!");
       }

       public void down() {

           System.out.println("The seat is down!");
       }
   }
}
Bicycle여기에는 자전거 수업이 있습니다 . 2개의 필드와 1개의 메소드가 있습니다 start(). 중첩된 내부 클래스 - 3일반 클래스와의 차이점은 내부에 코드가 작성된 두 개의 클래스가 있다는 것입니다. 이 클래스는 (핸들) 및 (좌석) Bicycle클래스입니다 . 이것은 본격적인 클래스입니다. 보시다시피 각 클래스에는 고유한 메서드가 있습니다. 이 시점에서 질문이 생길 수 있습니다. 왜 한 클래스를 다른 클래스 안에 넣었습니까? 왜 내부로 만드나요? 좋아, 프로그램에서 운전대와 좌석에 대해 별도의 클래스가 필요하다고 가정해 보겠습니다. 하지만 중첩할 필요는 없습니다! 정규 수업을 진행하실 수 있습니다. 예를 들어 다음과 같습니다. HandleBarSeat
public class HandleBar {
   public void right() {
       System.out.println("Steering wheel to the right!");
   }

   public void left() {

       System.out.println("Steering wheel left");
   }
}

public class Seat {

   public void up() {

       System.out.println("The seat is up!");
   }

   public void down() {

       System.out.println("The seat is down!");
   }
}
아주 좋은 질문입니다! 물론 기술적인 제한은 없습니다. 이런 식으로 할 수 있습니다. 특정 프로그램의 관점과 해당 프로그램의 의미에서 클래스를 올바르게 설계하는 것에 관한 것입니다. 내부 클래스는 프로그램에서 다른 엔터티와 불가분하게 연결된 특정 엔터티를 강조하기 위한 클래스입니다. 핸들, 시트, 페달은 자전거의 구성 요소입니다. 자전거와 분리하면 의미가 없습니다. 이러한 모든 클래스를 별도의 공개 클래스로 만들면 프로그램은 예를 들어 다음 코드를 가질 수 있습니다.
public class Main {

   public static void main(String[] args) {
       HandleBar handleBar = new HandleBar();
       handleBar.right();
   }
}
음... 이 코드의 의미는 설명하기조차 어렵습니다. 우리는 일종의 이상한 자전거 핸들바를 가지고 있습니다(왜 그것이 필요한가요? 솔직히 말해서 모르겠어요). 그리고 이 핸들은 자전거 없이도... 저절로 오른쪽으로 회전합니다. 핸들의 본질과 자전거의 본질을 분리함으로써 우리는 프로그램의 논리를 상실했습니다. 내부 클래스를 사용하면 코드가 완전히 다르게 보입니다.
public class Main {

   public static void main(String[] args) {

       Bicycle peugeot = new Bicycle("Peugeot", 120);
       Bicycle.HandleBar handleBar = peugeot.new HandleBar();
       Bicycle.Seat seat = peugeot.new Seat();

       seat.up();
       peugeot.start();
       handleBar.left();
       handleBar.right();
   }
}
콘솔 출력:

Сиденье поднято выше!
Поехали!
Руль влево!
Руль вправо!
갑자기 일어난 일이 이해가 되었습니다! :) 우리는 자전거 객체를 만들었습니다. 우리는 핸들과 좌석이라는 두 개의 "하위 개체"를 만들었습니다. 우리는 편의를 위해 좌석을 더 높게 올렸고 출발했습니다. 우리는 가야 할 곳으로 굴러서 조종했습니다! :) 우리에게 필요한 메소드는 필요한 객체에 대해 호출됩니다. 모든 것이 간단하고 편리합니다. 이 예에서 핸들바와 좌석을 강조 표시하면 캡슐화가 향상되고(해당 클래스 내에서 자전거 부품에 대한 데이터가 숨겨짐) 더 자세한 추상화를 생성할 수 있습니다. 이제 또 다른 상황을 살펴보겠습니다. 자전거와 부품 상점을 모델링하는 프로그램을 만들고 싶다고 가정해 보겠습니다. 중첩된 내부 클래스 - 4이 상황에서는 이전 솔루션이 실패합니다. 부품 매장 내에서 자전거의 각 부품은 자전거의 본질과는 별개로 의미를 갖습니다. 예를 들어, "구매자에게 페달 판매", "새 좌석 구입" 등과 같은 방법이 필요합니다. 여기서 내부 클래스를 사용하는 것은 실수입니다. 새 프로그램 내에서 자전거의 각 개별 부분은 고유한 의미를 갖습니다. 이는 자전거의 본질과 분리되어 있으며 어떤 방식으로도 연결되지 않습니다. 내부 클래스를 사용해야 하는지, 아니면 모든 엔터티를 별도의 클래스로 분리해야 하는지 궁금하다면 주의해야 할 사항입니다. 객체 지향 프로그래밍은 실제 엔터티를 쉽게 모델링할 수 있다는 점에서 훌륭합니다. 이는 내부 클래스 사용 여부를 결정할 때 가이드로 사용할 수 있는 내용입니다. 실제 매장에서는 부품이 자전거와 분리되어 있습니다. 이는 정상적인 현상입니다. 이는 프로그램을 설계할 때 이것이 정확하다는 것을 의미합니다. 좋아요, 우리는 "철학"을 정리했습니다 :) 이제 내부 클래스의 중요한 "기술적" 기능에 대해 알아 보겠습니다. 반드시 기억하고 이해해야 할 사항은 다음과 같습니다.
  1. 내부 클래스의 객체는 "외부" 클래스의 객체 없이는 존재할 수 없습니다.

    이것은 논리적입니다. 이것이 바로 우리가 Seat내부 HandleBar클래스로 만든 이유입니다. 그래서 주인 없는 운전대와 좌석이 우리 프로그램의 여기저기에 나타나지 않을 것입니다.

    이 코드는 컴파일되지 않습니다.

    public static void main(String[] args) {
    
       HandleBar handleBar = new HandleBar();
    }

    다음과 같은 중요한 기능이 이어집니다.

  2. 내부 클래스의 객체는 "외부" 클래스의 변수에 접근할 수 있습니다.

    예를 들어, 클래스에 시트포스트의 직경이라는 Bicycle변수를 추가해 보겠습니다 .int seatPostDiameter

    그런 다음 내부 클래스에서 좌석 매개변수를 알려주는 Seat메서드를 만들 수 있습니다 .getSeatParam()

    public class Bicycle {
    
       private String model;
       private int weight;
    
       private int seatPostDiameter;
    
       public Bicycle(String model, int weight, int seatPostDiameter) {
           this.model = model;
           this.weight = weight;
           this.seatPostDiameter = seatPostDiameter;
    
       }
    
       public void start() {
           System.out.println("Go!");
       }
    
       public class Seat {
    
           public void up() {
    
               System.out.println("The seat is up!");
           }
    
           public void down() {
    
               System.out.println("The seat is down!");
           }
    
           public void getSeatParam() {
    
               System.out.println("Seat parameter: seatpost diameter = " + Bicycle.this.seatPostDiameter);
           }
       }
    }

    이제 프로그램에서 다음 정보를 얻을 수 있습니다.

    public class Main {
    
       public static void main(String[] args) {
    
           Bicycle bicycle = new Bicycle("Peugeot", 120, 40);
           Bicycle.Seat seat = bicycle.new Seat();
    
           seat.getSeatParam();
       }
    }

    콘솔 출력:

    
    Параметр сиденья: диаметр подседельного штыря = 40

    주의하세요:새 변수는 가장 엄격한 수정자 - 로 선언됩니다 private. 그리고 여전히 내부 클래스에 접근할 수 있습니다!

  3. 내부 클래스 객체는 "외부" 클래스의 정적 메서드에서 생성될 수 없습니다.

    이는 내부 클래스의 디자인 기능으로 설명됩니다. 내부 클래스에는 매개변수가 있는 생성자가 있을 수도 있고 기본 생성자만 있을 수도 있습니다. 그러나 이에 관계없이 내부 클래스의 객체를 생성하면 "외부" 클래스의 객체에 대한 참조가 조용히 전달됩니다. 결국 그러한 객체의 존재는 전제 조건입니다. 그렇지 않으면 내부 클래스의 객체를 생성할 수 없습니다.

    그러나 외부 클래스 메서드가 정적이라면 외부 클래스 객체가 전혀 존재하지 않을 수도 있습니다! 이는 내부 클래스의 논리가 깨짐을 의미합니다. 이러한 상황에서 컴파일러는 오류를 발생시킵니다.

    public static Seat createSeat() {
    
       //Bicycle.this cannot be referenced from a static context
       return new Seat();
    }
  4. 내부 클래스에는 정적 변수와 메서드가 포함될 수 없습니다.

    여기서의 논리는 동일합니다. 정적 메서드와 변수는 개체가 없더라도 존재하고 호출될 수 있습니다.

    그러나 "외부" 클래스의 객체가 없으면 내부 클래스에 접근할 수 없습니다.

    명백한 모순이다! 따라서 내부 클래스에는 정적 변수와 메서드가 존재하는 것이 금지됩니다.

    컴파일러는 이를 생성하려고 할 때 오류를 발생시킵니다.

    public class Bicycle {
    
       private int weight;
    
    
       public class Seat {
    
           //inner class cannot have static declarations
           public static void getSeatParam() {
    
               System.out.println("Seat parameter: seatpost diameter = " + Bicycle.this.seatPostDiameter);
           }
       }
    }
  5. 내부 클래스 객체를 생성할 때 해당 객체의 액세스 한정자가 중요한 역할을 합니다.

    내부 클래스는 표준 액세스 한정자( public, 및 ) private로 표시할 수 있습니다 .protectedpackage private

    왜 중요 함?

    이는 프로그램에서 내부 클래스를 인스턴스화할 수 있는 위치에 영향을 줍니다.

    클래스가 Seat로 선언 되면 public다른 클래스에서 해당 객체를 만들 수 있습니다. 유일한 요구 사항은 "외부" 클래스의 개체도 존재해야 한다는 것입니다.

    그건 그렇고, 우리는 이미 여기서 이 작업을 수행했습니다.

    public class Main {
    
       public static void main(String[] args) {
    
           Bicycle peugeot = new Bicycle("Peugeot", 120);
           Bicycle.HandleBar handleBar = peugeot.new HandleBar();
           Bicycle.Seat seat = peugeot.new Seat();
    
           seat.up();
           peugeot.start();
           handleBar.left();
           handleBar.right();
       }
    }

    HandleBar우리는 에서 내부 클래스에 쉽게 접근했습니다 Main.

    내부 클래스를 로 선언하면 private"외부" 클래스 내의 객체 생성에만 액세스할 수 있습니다.

    더 이상 외부에서 객체를 생성 Seat할 수 없습니다 .

    private class Seat {
    
       //methods
    }
    
    public class Main {
    
       public static void main(String[] args) {
    
           Bicycle bicycle = new Bicycle("Peugeot", 120, 40);
    
           //Bicycle.Seat has a private access in 'Bicycle'
           Bicycle.Seat seat = bicycle.new Seat();
       }
    }

    아마 당신은 이미 논리를 이해하고 있을 것입니다. :)

  6. 내부 클래스의 액세스 한정자는 일반 변수와 동일하게 작동합니다.

    수정자는 protected하위 클래스 및 동일한 패키지에 있는 클래스의 클래스 변수에 대한 액세스를 제공합니다.

    protected내부 클래스에서도 동일하게 작동합니다. protected내부 클래스 객체를 생성할 수 있습니다:

    • "외부" 클래스 내부;
    • 그 후손 클래스에서;
    • 동일한 패키지에 있는 클래스에 있습니다.

    내부 클래스에 접근 한정자( )가 없으면 package private내부 클래스의 객체를 생성할 수 있습니다.

    • "외부" 클래스 내부;
    • 같은 패키지에 있는 클래스에서.

    오랫동안 수식어에 익숙하셨기 때문에 여기서는 문제가 없을 것입니다.

지금은 그게 전부입니다 :) 하지만 긴장을 풀지 마세요! 내부 중첩 클래스는 향후 강의에서 계속해서 살펴볼 상당히 광범위한 주제입니다. 이제 우리 코스의 내부 수업에 대한 강의를 복습하실 수 있습니다 . 다음번에는 정적 중첩 클래스에 대해 이야기하겠습니다.
코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION