본문 바로가기

BACKEND/JAVA

[이것이 자바다] 9장 중첩 클래스와 중첩 인터페이스

중첩 클래스

클래스 내부에 선언되는 위치에 따라서 두 가지로 분류된다.

중첩 클래스를 사용하면 클래스의 멤버를 쉽게 사용할 수 있고 외부에는 중첩 관계 클래스를 감춤으로 코드의 복잡성을 줄일 수 있다.

중첩 클래스도 컴파일 과정에서 바이트코드가 별도로 생성된다.

선언 위치에 따른 분류 선언 위치 설명
멤버 클래스 인스턴스 멤버 클래스 class A {
   class B { ...}
}
A 객체를 생성해야만 사용할 수 있는 B 중첩 클래스
정적 멤버 클래스 class A {
  static class B { ...}
}
A 클래스로 바로 접근할 수 있는 B 중첩 클래스
로컬 클래스 class A {
   void method() {
      class B { ...}
      }
}
method() 가 실행할 때문 사용할 수 있는 B 중첩 클래스

 

* 인트턴스 멤버 클래스

A객체를 생성해야지 B 객체도 같이 생성되며 클래스 내부에는 일반 클래스와 같이 필드,생성자,메서드가 올 수 있다.

public class A {
    class B {
        int field1 = 1;

        static int field2 = 2;

        B() {
            System.out.println("B 생성자 실행");
        }

         void method1() {
             System.out.println("B-method1() 실행");
         }

         static void method2() {
             System.out.println("B-static-method2() 실행");
         }
    }
}
public class Main {
    public static void main(String[] args) {
        A.B.method2();  // B-static-method2() 실행

        A a = new A();
        A.B b = a.new B();  // B 생성자 실행

    }
}
// 컴파일 하면 생성되는 바이트 코드 파일(.class)
A$B.class               => A : 바깥 클래스 / B : 멤버 클래스(로컬 클래스)
A.class

 

* 정적 멤버 클래스

static 키워드로 선언된 클래스를 의미하며 모든 종류의 필드와 메소드를 선언할 수 있다.

class A {
	static class C {
    	C(){  }				// 생성자
        int field1;			// 인스턴스 필드
        static int field2;		// 정적 필드
        void method1() {   }		// 인스턴스 메소드
        static void method2() {  }	// 정적 메소드
        }
}
A.C c = new A.C();	
c.field1 = 3;		// 인스턴스 필드 사용
c.method()1;		// 인스턴스 메소드 호출
A.C.field2 = 3;		// 정적 필드 사용
A.C.method2();		// 정적 메소드 호출

클래스가 메모리로 로딩되면 정적 멤버를 바로 사용할 수 있다.

> 클래스 로딩 시점

  • 클래스의 인스턴스가 생성될 때
  • 클래스의 정적 변수가 사용될 때
  • 클래스의 정적 메소드가 호출될 떄

* 로컬 클래스

생성자 또는 메소드 내부에서 선언된 클래스

생성자와 메소드가 실행될 동안에만 객체를 생성할 수 있다.

final 키워드를 붙이지 않아도 명사적으로 final 특성을 가지고 있어 값을 읽을 수만 있고 수정할 수 없다.

public class E {
    public E() {
        System.out.println("E 객체 생성");
    }

    void method() {

        int var = 1;
        
        class F {
            int field = 1;
            public F() {
                System.out.println("F 객체 생성");
            }

            void method1() {
                field = 2;
                System.out.println("F객체 field 값: " + field);
            }

            static void method2() {
                System.out.println("F객체 static 메서드 실행!");
            }
        }

        F f = new F();
        f.method1();

        F.method2();
    }
}
E$1F.class
E.class

 

 

* 익명 객체
책 내용 : 단독으로 생성할 수 없고 클래스를 상속하거나 인터페이스를 구현해야만 생성 가능
익명 객체는 필드의 초기값이나 로컬 변수의 초기값, 매개변수의 매개값으로 주로 대입
익명 자식객체에 새롭게 정의된 필드와 메소드는 익명 자식 객체 내부에서만 사용
익명 자식객체는 부모 타입 변수에 대입되므로 부모 타입에 선언된 것만 사용할 수 있기 때문

 

* 익명 객체를 사용하는 이유

일회성으로 사용하는 구현 객체를 만들기 위해 소스 파일을 만들고 클래스를 선언하는 것은 비효율적 → 자바에서 소스 파일을 만들지 않고도 구현 객체를 만들 수 있는 방법을 제공

 

* 익명 클래스

객체 사용시에 클래스의 선언과 객체 생성이 동시에 이루어져 일회성으로 딱 하나의 객체만 필요할 경우 사용된다.
이름이 없는 클래스이므로 상속 또는 인터페이스에 대해서 명시를 해줘야 한다.
부모클래스이름 변수명 = new 부모클래스 이름 {  ... 내용 구현 ...  };
인터페이스이름 변수명 = new 인터페이스 이름 {  ... 내용 구현 ...  };

 

* 익명함수
- 무엇일까
    -> 이름이 없는 클래스로 클래스 정의와 동시에 객체를 생성
    -> 클래스의 선언과 객체의 생성을 동시에 하기 때문에 단 한 번만 사용될 수 있고 오직 단 하나의 객체만을 생성할 수 있는 일회용 클래스
    -> 생성자를 가질 수 없으며 단 하나의 클래스를 상속받거나 하나의 인터페이스만을 구현할 수 있다.
    -> 리턴 값을 가질 수 있으며 매개변수를 가진다.
    -> 익명클래스에 생성된 메소드나 필드는 익명클래스 밖에선 접근 불가
-> 접근이 가능한 경우 : 메소드의 매개변수나 로컬변수를 익명 객체에서 사용할 때 가능 -> 이유 : 메소드 내에서 생성된 익명 객체는 실행이 끝나고 힙 메모리에 존재
- 왜 쓰는 것일까
     -> 자바에서 함수를 일급객체로 취급하지 못하지만, 익명 클래스를 사용하여 메서드 자체는 못넘기고 메서드를 감싼 익명 클래스를 넘긴다.
     -> 클래스 정의와 구현을 동시에 하기 때문에 코드가 적어진다.
     -> 재사용성이 없을 때
     -> 특정 구현 부분만 따로 사용하거나, 부분적으로 기능을 일시적으로 바꿔야 할 경우가 생길 때
- 언제 쓰는 것일까
    -> 일시적으로 한번만 사용되고 버려져도 되는 객체일 때
    -> 자식 클래스에 상속시켜 사용하지 않고 익명으로 코드 내에 선언
    -> 객체를 간편하게 생성할 목적
> 일급 객체
- 조건
    - 변수나 데이터에 담을 수 있어야 한다.
    - 함수의 파라미터로 전달 할 수 있어야 한다.
    - 함수의 리턴값으로 사용할 수 있어야 한다.


- 사용 방법ex
=> 괄호 안에는 필드나,메소드를 선언하거나 부모 클래스의 메소드를 재정의하는 내용 -> 생성자를 생성할 수 없다는 가장 큰 차이

IMax iMax = new IMax() {
@Override
// 외부에서 접근할 수 있도록 public 사용
public int getMax(int x, int y) {
return (x > y) ? x : y;
}
};  // 객체를 생성하는 것이므로 ; 필요

익명 자식 객체에 새롭게 정의된 필드와 메소드는 익명 자식 객체 내부에서만 사용,외부에서는 접근 불가
-> 익명 자식 객체는 부모 타입 변수에 대입되므로 부모 타입에 선언된 것만 사용할 수 있기 때문
익명 객체 내부에서 메소드의 매개 변수나 로컬 변수를 사용할 경우, final 특성을 가져야 한다.