본문 바로가기
TIL

Java - 내부 클래스(중첩 클래스), 익명 클래스 (22.11.07 TIL)

by winteringg 2022. 11. 7.

다시 돌아온 자바 개념서 정독 시간 ~_~


1. 내부 클래스 (Inner class)
1) 클래스 안의 클래스. 중첩 클래스 라고도 함.

선언 위치에 따른 분류 선언 위치 설명
멤버 클래스 인스턴스 멤버 클래스 class A {
   class B { ... }
}
A 객체를 생성해야만 사용할 수 있는 B 중첩 클래스.
주로 외부 클래스의 인스턴스멤버들과 관련된 작업에 사용될 목적으로 선언됨.
정적 멤버 클래스 class A {
   static class B { ... }
}
A 클래스로 바로 접근할 수 있는 B 접근 클래스.
주로 외부 클래스의 static 멤버, 특히 static 메서드에서 사용될 목적으로 선언됨.
로컬 클래스 class A {
   void method() {
      class B { ... }
   }
}
method() 가 실행될 때만 사용할 수 있는 B 중첩 클래스.
외부 클래스의 메서드나 초기화블럭 안에 선언하며, 선언된 영역 내부에서만 사용될 수 있음.
익명 클래스   클래스의 선언과 객체의 생성을 동시에 하는 이름없는 일회용 클래스


2) 내부 클래스의 특징
  - 클래스 멤버인 중첩 클래스는 멤버 클래스, 메서드 내부에 선언된 중첩 클래스는 로컬 클래스.
  - 내부 클래스에서 외부 클래스의 멤버들을 객체 생성 없이도 접근 가능
  - 다른 클래스에서 내부 클래스를 쓰려면 외부 클래스의 객체를 먼저 생성해야 내부 클래스도 접근 가능함.
  - 외부에는 불필요한 관계 클래스를 감춤으로써 코드의 복잡성을 줄일 수 있음. (캡슐화)

3) 멤버 클래스
  - static 필드와 메서드를 선언할 수 없음.
  - static 멤버 클래스는 모든 종류의 필드와 메서드를 선언할 수 있음.
  - 인스턴스 멤버 클래스 예시

class A {
   /**인스턴스 멤버 클래스**/
   class B {       
     B() { }                      //생성자
     ine field1;                  //인스턴스 필드
     //static int field2;         //정적 필드 사용 불가
     void method1() { }           //인스턴스 메서드
     //static void method2() { }  //정적 메서드 사용 불가
  }
}

A a = new A();         //A클래스 객체를 생성해야 B클래스 접근 가능           
A.B b = a.new B();     //A에 중첩된 B클래스 라는 의미로 객체 생성
b.field1 = 3;          //참조변수 b로 인스턴스 필드 접근 가능
b.method1();


  - 정적 멤버 클래스 예시

class A {
   /**정적 멤버 클래스**/
   static class C {       
     C() { }                      //생성자
     ine field1;                  //인스턴스 필드
     static int field2;           //정적 필드 사용 가능
     void method1() { }           //인스턴스 메서드
     static void method2() { }    //정적 메서드 사용 가능
  }
}

A.C c = new A.C();       
c.field1 = 3;            //C 객체를 생성해야 인스턴스 필드 사용 가능
c.method1();             //C 객체를 생성해야 인스턴스 메서드 사용 가능
A.C.field2 = 3;          //C 객체를 만들지 않고도 접근 가능한 static 필드
A.C.method2();           //C 객체를 만들지 않고도 접근 가능한 static 메서드 호출

5) 로컬 클래스
  - 자바 7 이전 : 메서드 실행이 끝나도 로컬 클래스의 객체는 힙 메모리에 존재하고 계속 사용될 수 있음. 매개 변수나 로컬 변수는 메서드 실행이 끝나면 스택 영역에서 사라짐. 그렇기 때문에 로컬 클래스는 매개변수나 로컬 변수를 컴파일 시 로컬 클래스 내부에 복사해서 사용하며, 매개 변수나 로컬 변수를 final로 선언해 수정을 막음.
  - 자바 8 이후 : 로컬 클래스에서 사용될 경우 매개 변수와 로컬 변수는 final 특성을 자체적으로 가지기 때문에 final 키워드를 생략해도 되며, 이것은 로컬 클래스의 필드로 복사됨.
  - 로컬 클래스는 접근 제한자나 static 을 붙일 수 없음. (메서드 내부에서만 사용되므로 접근을 제한할 필요가 없음.)

void method() {
   /**로컬 클래스**/
   class D {
     D() { }                       //생성자
     int field1;                   //인스턴스 필드
     //static int field2;          //정적 필드 사용 불가
     void method1() { }            //인스턴스 메서드
     //static void method2() { }   //정적 메서드 사용 불가
   }
   D d = new D();         //D 객체를 생성해야 객체의 필드 접근 가능
   d.field1 = 3;
   d.method1();
}

6) 로컬 클래스의 상속

void method() {
  class DownloadThread extends Thread { ... }
  DownloadThread thread = new DownloadThread();
  thread.start();
}


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

public class Outter {
  String field = "Outter-field";
  void method() {
    System.out.println("Outter-method");
}

class Nested {
  String field = "Nested-field";
  void method() {
    System.out.println("Nested-method");
  }
  void print() {
    System.out.println(this.field);         //Nested 클래스의 참조가 됨.
    this.method();
    System.out.println(Outter.this.field);  //Outter 클래스의 참조가 됨.
    Outter.this.method();
  }
}

 

 

3. 내부 클래스의 제어자와 접근성
1) 기본 클래스에서는 defalut, public 제어자만 쓸 수 있지만, 내부 클래스의 제어자는 변수에 사용 가능한 제어자와 동일. (모든 제어자 사용 가능)

class Outer {
  private class InstanceInner {}
  protected static class StaticInner {}
  
  void myMethod() {
    class LocalInner {}
  }
}

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

public class A {
  //인스턴스 멤버 클래스
  class B {}
  
  //정적 멤버 클래스
  static class C {}
}

  - 만약 위 처럼 클래스 A 안에 B 내부 클래스, C 정적 내부 클래스가 있다고 할 때 사용 제한 범위는?

public class A {
//인스턴스 필드
  B field1 = new B();         //사용 가능
  C field2 = new C();         //사용 가능
  
  //인스턴스 메서드
  void method1() {
    B var1 = new B();         //사용 가능
    C var2 = new C();         //사용 가능
    
  //정적 필드 초기화
  //static B field3 = new B();    //에러. B는 인스턴스 멤버이기 때문에 A객체가 있어야 사용 가능.
  static C field4 = new C();      //static 으로 선언된 C는 단독으로 사용 가능
  
  //정적 메서드
  //B var1 = new B();       //에러. B는 인스턴스 멤버이기 때문에 A객체가 있어야 사용 가능.
  C var2 = new C();         //static 으로 선언된 C는 단독으로 사용 가능

 

3. 중첩 인터페이스
1) 클래스 내부에 선언한 인터페이스
2) 해당 클래스와 긴밀한 관계를 맺는 구현 클래스를 만들기 위해 선언.
  - 외부에서는 사용하지 않고 클래스 내에서만 사용할 수 있는 인터페이스.
3) 메소드 실행이 끝나도 구현 객체는 힙 메모리에 존재하지만 매개 변수나 로컬 변수는 메소드 실행이 끝나면 스택 영역에서 사라지므로 로컬 클래스와 동일하게 처리.

public class Button
  OnClickListener listener;           //인터페이스 타입 필드
  
  void setOnClickListener(OnClickListener listener) {   //매개변수의 다형성. 다양한 구현객체가 옴.
    this.listener = listener;                           //외부에서 구현객체를 대입받아서 필드에 대입시키기
  }
  
  void touch() {
    listener.onClick();               //구현 객체인 onClick()메서드 호출
  }
   
  interface OnClickListener {         // 중첩 인터페이스
    void onClick();
  }
}

 

4. 익명 클래스 (anonymous class)
1) 이름이 없는 일회용 객체. 정의와 생성을 동시에 함.
2) 단독으로 생성할 수 없고 클래스를 상속하거나 인터페이스를 구현해야만 생성할 수 있음.
3) UI 이벤트 처리 객체, 스레드 객체를 간편하게 생성할 목적으로 주로 활용됨.
4) 익명 객체는 필드의 초기값이나 로컬 변수의 초기값, 매개 변수의 매개값으로 주로 대입됨.
  - 메소드의 매개 변수가 부모 타입일 경우 메소드 호출 코드에서 익명 자식 객체를 생성해서 매개값으로 대입할 수도 있음.
  - 부모클래스(매개값){ }은 부모 클래스를 상속해서 중괄호와 같이 자식 클래스를 선언하라는 뜻이고 new 연산자는 이렇게 선언된 자식 클래스를 객체로 생성하는 것.
5) 익명 자식 객체 생성

//익명 자식 객체 생성

부모클래스 필드 = new 부모클래스(매개값) {
   // 필드
   // 메소드
};

6) 익명 자식 객체가 부모 타입 필드 초기값으로 대입된 예시

class A {
 //A 클래스의 필드 선언
 Parent field = new Parent() {   //객체 끝에 중괄호가 붙은 것은 익명 자식 객체 생성하는 것.
    int childField;
    void childMethod() { }
    @Override                    //Parent의 메서드를 오버라이딩
    void parentMethod() { }
 };
}

7) 로컬변수 초기값으로 자식 객체가 대입된 예시

class A {
  void method() {
    //로컬변수 선언
    Parent localVar = new Parent() {
       int childField;
       void childMethod() { } 
       @Override                     //Parent의 메서드를 오버라이딩
       void parentMethod() { }

8) 메서드의 매개값으로 익명 자식 객체를 생성

class A {
  void method1(Parent parent) { }
  
  void method2() {
    method1 (                     //method1() 메서드 호출
      new Parent() {              //method1()의 매개값으로 익명 하위 클래스를 이용해서 객체로 대입
         int childField;
         void childMethod() { }
         @Override
         void parentMethod() { }
       }
   };
}


9) 익명 구현 객체 생성

//익명 구현 객체 생성

부모클래스 [필드|변수] = new 부모클래스(매개값, ...) {
   //인터페이스에 선언된 추상 메서드의 실체 메서드 선언
   // 필드
   // 메소드
};


10) 일반 클래스와의 차이점은 생성자를 선언할 수 없다는 것임.
11) 익명 객체에 새롭게 정의된 필드와 메서드
  - 익명 객체 내부에서만 사용됨.
  - 외부에서는 익명 객체의 필드와 메서드에 접근할 수 없음. (익명 객체는 이름이 없기 때문에 항상 부모 타입 변수에 대입되므로 부모 타입에 선언된 것만 사용할 수 있음)

 

 

 

 

참고 : [한빛미디어] 이것이 자바다 (신용권의 Java 프로그래밍 정복) Chapter 9.중첩 클래스
참고 : [도우출판] JAVA의 정석(3ND EDITION)-자바의 정석 최신 Java 8.0 포함 Chapter 7.내부 클래스

댓글