[Java] 09. 중첩 선언과 익명 객체

JK·2024년 4월 16일
0

[Java]

목록 보기
9/11

중첩 클래스

클래스가 여러 클래스와 관계를 맺는 경우에는 클래스를 각각 독립적으로 선언하는 것이 좋다. 하지만 클래스가 특정 클래스와만 긴밀한 관계를 맺는 경우, 중첩 클래스로 선언하는 것이 유지 보수에 도움이 될 수 있다.

중첩 클래스(Nested Class)는 클래스 내부에 선언한 클래스를 말한다. 중첩 클래스의 장점으로는 클래스의 멤버를 쉽게 사용이 가능하다는 것과, 외부에 중첩 관계 클래스를 감춤으로써 코드의 복잡성을 줄일 수 있다는 점이 있다.

중첩 클래스는 선언 위치에 따라 아래와 같이 두 가지로 분류한다.

선언 위치에 따른 분류선언 위치객체 생성 조건
멤버 클래스인스턴스 멤버 클래스클래스 내부외부 클래스의 객체를 생성해야만 내부 클래스 객체 생성 가능
정적 멤버 클래스클래스 내부외부 클래스 객체를 생성하지 않아도 내부 클래스 객체 생성 가능
로컬 클래스클래스의 메소드 내부해당 메소드가 실행 중일 때만 객체 생성 가능

중첩 클래스도 하나의 클래스이기 때문에 컴파읾하면 바이트코드 파일(.class)이 별도로 생성된다. 멤버 클래스일 경우는 파일명이 '바깥클래스명$멤버클래스명.class'가 되고, 로컬 클래스일 경우는 파일명이 '바깥클래스명$1로컬클래스명.class'가 된다.

인스턴스 멤버 클래스

[public] class A {
    [public | private] class B {
    }
}

위와 같이 A 클래스의 멤버로 선언된 B 클래스를 인스턴스 멤버 클래스라고 한다. 접근 제한자에 따른 인스턴스 멤버 클래스의 접근 범위는 아래와 같다.

구분접근 범위
public class B {}다른 패키지에서 B 클래스를 사용할 수 있다
class B {}같은 패키지에서만 B 클래스를 사용할 수 있다
private class B {}A 클래스 내부에서만 B 클래스를 사용할 수 있다

인스턴스 멤버 클래스는 주로 클래스의 내부에서 사용되기 때문에 private 접근 제한을 갖는 것이 일반적이다. 인스턴스 멤버 클래스의 객체는 인스턴스 필드값, 생성자, 인스턴스 메소드에서 생성이 가능하다.

인스턴스 멤버 클래스 내부에는 일반 클래스와 같이 필드, 생성자, 메소드를 선언할 수 있고, Java 17 버전 부터는 정적 필드와 정적 메소드도 선언할 수 있다.

정적 멤버 클래스

[public] class A {
    [public ? private] static class B {
    }
}

위와 같이 static 키워드와 함께 A 클래스의 멤버로 선언된 B 클래스를 정적 멤버 클래스라고 한다. 접근 제한자에 따른 정적 멤버 클래스의 접근 범위는 아래와 같다.

구분접근 범위
public static class B {}다른 패키지에서 B 클래스를 사용할 수 있다
static class B {}같은 패키지에서만 B 클래스를 사용할 수 있다
private static class B {}A 클래스 내부에서만 B 클래스를 사용할 수 있다

정적 멤버 클래스는 A 클래스 내부보다 클래스 외부에서 A 클래스와 함께 사용되는 경우가 많기 때문에 주로 default 또는 public 접근 제한을 가진다. 정적 멤버 클래스의 객체는 클래스 내부 어디든 생성기 가능하다.

로컬 클래스

로컬 클래스는 생성자 또는 메소드 내부에 선언된 클래스를 말한다. 그렇기 때문에 생성자와 메소드가 실행 중일 동안에만 객체를 생성할 수 있다.

메소드 내부에 선언된 로컬 클래스에는 일반 클래스와 같이 필드, 생성자, 메소드를 선언할 수 있고, Java 17 버전부터는 정적 필드와 정적 메소드도 선언할 수 있다.

로컬 클래스에서 로컬 변수를 사용할 경우, 로컬 변수는 final 특성을 갖게 되는데, 로컬 클래스 내부에서는 기본적으로 값을 변경할 수 없기 때문이다. Java 7 버전까지는 명시적으로 로컬 변수에 final 키워드를 붙여야 했지만, Java 8 버전부터는 final 키워드를 붙이지 않아도 된다.

바깥 멤버 접근

중첩 클래스는 바깥 클래스와 긴밀한 관계를 맺으면서 바깥 클래스의 멤버(필드, 메소드)에 접근할 수 있다. 하지만 중첩 클래스가 선언된 형태에 따라 접근 제한이 있을 수도 있다.

바깥 클래스의 멤버 접근 제한

정적 멤버 클래스 내부에서는 바깥 클래스의 필드와 메소드를 사용할 때 아래와 같은 제한이 따른다.

구분바깥 클래스의 사용 가능한 멤버
인스턴스 멤버 클래스바깥 클래스의 모든 필드와 메소드
정적 멤버 클래스바깥 클래스의 정적 필드와 정적 메소드

바깥 클래스의 객체 접근

중첩 클래스 내부에서 this는 해당 중첩 클래스의 객체를 말한다. 만약 중첩 클래스 내부에서 바깥 클래스의 객체를 얻으려면 바깥 클래스의 이름에 this를 붙여야 한다.

중첩 인터페이스

class A {
    [public | private] [static] interface B {
        // 상수 필드
        // 추상 메소드
        // 디폴트 메소드
        // 정적 메소드
    }
}

중첩 인터페이스는 클래스의 멤버로 선언된 인터페이스를 말한다. 해당 클래스와 긴밀한 관계를 맺는 구현 객체를 만들기 위해 클래스 내부에 선언한다.

중첩 인터페이스는 아래와 같이 안드로이드같은 UI 프로그램에서 이벤트를 처리할 목적으로 많이 활용한다.

public class Button {
    // 정적 중첩 인터페이스
    public static interface ClickListener {
        // 추상 메소드
        void onClick();
    }
}

익명 객체

익명 객체는 말 그대로 이름이 없는 객체를 뜻한다. 명시적으로 클래스를 선언하지 않기 때문에 쉽게 객체를 생성할 수 있다는 장점이 있다. 주로 필드값, 로컬 변수값, 매개변수값으로 사용한다.

클래스를 상속하거나 인터페이스를 구현해야만 생성이 가능한데, 클래스를 상속해서 만들 경우 익명 자식 객체라고 하고 인터페이스를 구현해서 만들 경우 익명 구현 객체라고 한다.

익명 자식 객체

부모타입 변수명 = new 부모생성자(매개값, ...) {
    // 필드
    // 메소드
}

부모 클래스를 상속 받아 위와 같이 생성한다. 부모 타입의 필드, 로컬 변수, 매개변수의 값으로 대입할 수 있다. 중괄호 블록 안에는 주로 부모 메소드를 재정의하는 코드를 작성한다.

익명 구현 객체

인터페이스 변수명 = new 인터페이스() {
    // 필드
    // 메소드
}

인터페이스를 구현해서 위와 같이 생성한다. 인터페이스 타입의 필드, 로컬 변수, 매개변수의 값으로 대입할 수 있다. 안드로이드와 같은 UI 프로그램에서 이벤트 처리 객체로 많이 사용한다. 중괄호 블록 안에는 주로 인터페이스의 추상 메소드를 재정의하는 코드를 작성한다.


"한빛 미디어 출판 도서, 이것이 자바다"를 읽고 학습한 내용을 토대로 작성되었습니다.

0개의 댓글