nested class,interface

4 분 소요

중첩 클래스와 중첩 인터페이스란?

  • 중첩 클래스(Nested class) 란 클래스 내부에 선언한 클래스

    • 중첩클래스 사용 시 두 클래스의 멤버들을 서로 쉽게 접근할수 있다는 장점과 외부에는 불필요한 관계 클래스를 감춤으로써 코드의 복잡성이 줄어듬.
class ClassName{
    class NestedClassName{ // 중첩클래스
    }
}

class ClassName{
    interface NestedInterfaceName{ //중첩 인터페이스
    }
} 
  • 인터페이스도 클래스 내부에 선언가능함. 이런 인터페이스를 중첩 인터페이스라고 함.
    • 인터페이스를 클래스 내부에 선언하는 이유는 해당 클래스와 긴밀한 관계를 맺는 구현 클래스를 만들기 위해서이며, 중첩 인터페이스는 주로 UI 프로그래밍에서 이벤트 처리 목적으로 많이 활용 됨.

중첩 클래스

  • 중첩 클래스는 클래스 내부에 선언되는 위치에 따라 두가지로 분류 됨.
  • 클래스의 멤버로서 선언되는 중첩 클래스를 멤버 클래스, 메소드 내부에서 선언되는 중첩클래스를

로컬 클래스

  • 멤버 클래스는 클래스나 객체가 사용중이라면 언제든지 재사용이 가능
    • 로컬 클래스는 메소드 실행 시에만 사용되고 메소드 실행 종료 시 없어짐
선언위치에 따른 분류 분류 선언위치 설명
멤버 클래스 인스턴스 멤버 클래스 class A{ class B {…} } A객체를 생성해야만 사용할 수 있는 B중첩 클래스
멤버 클래스 정적 멤버 클래스 class A { static class B {…} } A 클래스로 바로 접근할 수 있는 B중첩 클래스
로컬 클래스   class A{ void method() { class B{.. } } } method()가 실행할 때만 사용할 수 있는 B중첩 클래스
  • 멤버클래스도 하나의 클래스이기때문에 컴파일 시 바이트 코드 파일(.class) 이 별도로 생성 됨.
    • A(바깥클래스) $ B(멤버클래스).class
  • 로컬클래스의 경우 다음과 같이 생성됨.
    • A(바깥클래스) $1 B(로컬클래스).class

인스턴스 멤버 클래스

  • 인스턴스 멤버 클래스는 static 키워드 없이 선언된 클래스
  • 인스턴스 멤버 클래스는 인스턴스 필드와 메소드만 선언 가능하고, 정적 필드와 메소드는 선언이 불가함
class A{
    // 인스턴스 멤버 클래스
    class B{
    B(){} //생성자
    int field1; //인스턴스 필드
    void method1(){}  //인스턴스 메소드
    }
}
  • A클래스 외부에서 인스턴스 멤버 클래스 B의 객체를 생성하려면 먼저 A객체 생성 후 B객체를 생성해야함.
A a = new A();
A.B b = a.new B();
b.field1 =3;
b.method();

정적 멤버 클래스

  • 정적 멤버 클래스는 static 키워드로 선언된 클래스
  • 정적 멤버 클래스는 모든 종류의 필드와 메소드를 선언 가능
    class A{
      static class C {
      c() {} //생성자
      int field1; //인스턴스 필드
      static int field //정적 필드
      void method1(){} //인스턴스 메소드
      static void method2(){ } // 정적 메소드
      }
    }
    
  • A 클래스 외부에서 정적 멤버 클래스 C 객체를 생성하기 위해서는 A객체를 생성할 필요가 없고, 다음과 같이 C객체를 생성
A.C c = new A.C();
c.field1 = 3; //인스턴스 필드 사용
c.method1(); //인스턴스 메소드 호출
A.C.field2 = 3; //정적 필드 사용
A.C.method1(); //정적 메소드 호출

로컬 클래스

  • 중첩 클래스는 메소드 내에서도 선언 가능함. 이것을 로컬(local) 클래스라고 함
  • 로컬 클래스는 접근제한자 및 static 을 붙일수 없음.
    • 메소드 내부에서만 사용되므로 접근을 제한할 필요가 없음. 로컬 클래스 내부에는 인스턴스 필드와 메소드만 선언이 가능하고, 정적 필드와 메소드는 선언 불가
      void method(){
      //로컬 클래스
      class D{
          D(){}     //생성자
          int field1;     //인스턴스 필드
          void method1(){}
      }
      D d = new D();
      d.field1 =3;
      d.method1();
      }
      
    • 로컬 클래스는 메소드가 실행될 때 메소드 내에서 객체를 생성하고 사용해야 함. (주로 비동기 처리를 위해 스레드 객체 만들때 사용함)

중첩 클래스의 접근 제한

바깥 필드와 메소드에서 사용 제한

  • 멤버 클래스가 인스턴스 또는 정적으로 선언됨에 따라 바깥 클래스의 필드와 메소드에 사용 제한이 생김
    • 인스턴스 멤버 클래스는 바깥 클래스의 인스턴스 필드의 초기값이나 인스턴스 메소드에서 객체를 생성할수 있으나, 정적 필드의 초기값이나 정적 메소드에서는 객체를 생성할 수 없다. 반면 정적 멤버 클래스는 모든 필드의 초기값이나 모든 메소드에서 객체를 생성할 수 있다.

멤버 클래스에서 사용 제한

  • 멤버 클래스가 인스턴스 또는 정적으로 선언됨에 따라 멤버클래스 내부에서 바깥클래스의 필드와 메소드를 접근할때에도 제한이 따름
    • 인스턴스 멤버 클래스 안에서는 바깥 클래스의 모든 필드와 모든 메소드에 접근할 수 있지만, 정적 멤버 클래스 안에서는 바깥 클래스의 정적 필드와 메소드에만 접근 가능하다.

로컬 클래스에서 사용 제한

  • 로컬 클래스에서 사용가능한 것은 final 로 선언된 매개변수와 로컬변수임.
    • final 선언을 하지 않아도 여전히 값을 수정할 수 없는 final 의 특성을 가지고있음. final 키워드 존재 여부의 차이점은 로컬 클래스의 복사 위치이다. final 키워드가 있다면 로컬 클래스의 메소드 내부에 복사 되지만, final 키워드가 없다면 로컬 클래스의 필드로 복사됨.
  • 로컬 클래스에서 사용된 매개변수와 로컬 변수는 모두 final 특성을 가짐.

중첩클래스에서 바깥 클래스 참조 얻기

  • 클래스 내부에서 this 는 객체 자신의 참조이나, 중첩 클래스에서 this 키워드는 바깥 클래스가 아닌 중첩 클래스의 객체 참조임.
    • 따라서 중첩 클래스 내부에서 this.필드, this.메소드() 를 호출하면 중첩클래스의 메소드가 사용됨.
바깥클래스.this.필드;
바깥클래스.this.메소드();
  • 중첩클래스 내부에서 바깥클래스의 객체 참조를 얻으려면 바깥클래스의 이름을 this 앞에 붙여주면 됨.

중첩 인터페이스

  • 중첩 인터페이스는 클래스의 멤버로 선언된 인터페이스를 말한다.
    • 인터페이스를 클래스 내부에 선언하는 이유는 해당 클래스와 긴밀한 관계를 맺는 구현 클래스를 만들기 위해서임.
      class A{
      interface I {
          void method();
      }
      }
      

익명 객체

  • 익명(anonymous) 객체는 이름이 없는 객체를 뜻함.
    • 익명 객체는 단독으로 생성 불가하고, 클래스를 상속하거나, 인터페이스를 구현해야만 생성할 수 있음.
  • 익명객체는 필드의 초기값이나, 로컬 변수의 초기값, 매개변수의 매개값으로 주로 대입됨.

익명 자식 객체 생성

  • 자식 클래스가 재사용되지 않고, 오로지 해당 필드와 변수의 초기값으로만 사용할 경우라면 익명 자식 객체를 생성해서 초기값으로 대입하는것이 좋은 방법
    부모클래스 [필드|변수] = new 부모클래스(매개값, ... ){
      //필드
      //메소드
    }; //실행문이므로 끝에는 세미콜론';'을 반드시 붙여야 함.
    
  • 부모클래스(매개값, …){} 은 부모클래스를 상속해서 중괄호{}와 같이 자식 클래스를 선언하라는 뜻이고, new 연산자는 이렇게 선언된 자식 클래스를 객체로 생성한다. 부모클래스(매개값,…) 은 부모 생성자를 호출하는 코드로, 매개값은 부모 생성자의 매개변수에 맞게 입력하면 되고, 중괄호 {}내부에는 필드나 메소드를 선언하거나, 부모클래스의 메소드를 재정의(오버라이딩) 하는 내용이 , 일반 클래스와 차이점은 생성자를 선언할 수 없음.
class A{
    Parent field = new Parent(){ // A클래스의 필드 선언
    int childField;
    void childMethod(){}
    @Override  // Parent메소드를 오버라이딩
    void parentMethod(){} 
    };
}
  • 메소드의 매개변수가 부모타입일 경우 메소드 호출 코드에서 익명 자식 객체를 생성해서 매개값으로 대입 가능
    class A{
      void method(Parent parent){}
    
      void method2(){
          method1(
              new Parent(){
                  int childField;
                  void childMethod(){}
                  @Override
                  void parentMethod(){} 
              }
          );
      }
    }
    

    익명 자식 객체에 새롭게 정의된 필드와 메소드는 익명 자식 객체 내부에서만 사용되고, 외부에서는 필드와 메소드에 접근할 수 없다. 왜냐하면 익명 자식 객체는 부모타입 변수에 대입되므로 부모타입에 선언된 것만 사용할 수 있기 때문

익명 구현 객체 생성

인터페이스 [필드|변수] = new 인터페이스(){
    // 인터페이스에 선언된 추상 메소드의 실체 메소드 선언
    // 필드
    // 메소드
};

인터페이스 (){} 는 인터페이스를 구현해서 중괄호 {}와 같이 클래스를 선언하라는 뜻이고, new 연산자는 선언된 클래스를 객체로 생성한다. 중괄호 {} 에는 인터페이스에 선언된 모든 추상 메소드들의 실체 메소드를 작성해야 함. 필드와 메소드를 선언할 수 있지만, 실체 메소드에서만 사용이 가능하고 외부에서는 사용하지 못한다.

카테고리:

업데이트: