본문 바로가기

p-languages/java

java/ 내부 클래스의 종류와 내부 클래스를 사용함으로써 얻는 장점

내부 클래스 Inner Class  =  이너 클래스, 중첩 클래스

내부 클래스는 클래스나 인터페이스 내에서 선언된 클래스를 말한다. 외부 클래스와 내부 클래스가 서로 연관되어 있을 때 사용할 수 있으며 클래스가 여러 클래스와 관계를 맺는 경우가 아닌, 특정 클래스와 관계를 맺는 경우에 내부 클래스로 선언하는 것을 권장한다. 

 

내부 클래스는 선언 위치에 따라 멤버 내부 클래스, 지역 내부 클래스, 익명 내부 클래스로 나뉠 수 있다. 클래스의 멤버로서 선언되는 클래스를 멤버 내부 클래스, 메서드 내부에서 선언되는 클래스를 지역 내부 클래스, new 연산자로 객체 생성과 동시에 선언되는 익명 내부 클래스라고 한다.

 


멤버 내부 클래스 - 인스턴스 멤버 클래스

 

static 키워드 없이 클래스 내부에 선언된 클래스이다. 클래스의 멤버로 인스턴스 필드와 메서드만 선언이 가능하고, 정적 멤버는 가질 수 없다.

 

public class Outer { //외부 클래스 선언
    static int a = 10;
    int b = 20;
    private int c = 30;

    void outerInstanceMethod(){
        System.out.println("외부 클래스의 인스턴스 메서드");
    }

    static void outerStaticMethod(){
        System.out.println("외부 클래스의 정적 메서드");
    }

    class InstanceInner{
        //static int d = 40; 인스턴스 멤버 클래스 안에서 static 사용 불가
        int e = 50;
        private int f = 60;
        
        InstanceInner(){}
        
        InstanceInner(int num1, int num2, int num3){
            a = num1; //외부 클래스의 필드를 불러오기 위해서 this.를 사용하지 않는다
            c = num2;
            this.f = num3; //내부 클래스 자신의 필드를 불러오기 위해 this.를 사용한다
        }
        
        void instanceMethod(){
            System.out.println("인스턴스 멤버 클래스의 인스턴스 메서드");
            System.out.printf("%d, %d, %d, %d, %d \n", a, b, c, e, f);
            //외부 클래스의 필드를 제한 없이 사용 가능
            outerStaticMethod();
            outerInstanceMethod();

        }
        /*
        static void staticMethod(){
            System.out.println("인스턴스 멤버 클래스 내부에 static 사용 불가");
        }
        */
    }

}

 

Outer 클래스 외부에서 인스턴스 멤버 클래스의 멤버를 사용하기 위해서는 먼저 Outer(외부 클래스)의 객체를 생성하고, 이어서 InstanceInner(내부 클래스)의 객체를 생성해주어야 한다.

 

public class Main {
    public static void main(String[] args) {
        Outer outer = new Outer();
        //외부 클래스의 객체 생성
        Outer.InstanceInner instanceInner = outer.new InstanceInner();
        //외부 클래스를 통해 내부 클래스를 불러와 내부 클래스의 객체 생성
        instanceInner.instanceMethod();
        //내부 클래스의 메서드 호출
    }
}
출력

인스턴스 멤버 클래스의 인스턴스 메서드
10, 20, 30, 50, 60
외부 클래스의 정적 메서드
외부 클래스의 인스턴스 메서드

 

헷갈리기 쉬운) 

인스턴스 멤버 클래스의 경우 내부 클래스 자신의 멤버로서 static 필드나 메서드를 선언할 수 없을 뿐, 외부 클래스에서 선언된 static 필드나 메서드는 객체 생성 없이 바로 사용할 수 있다는 것에 주의하자.

 


멤버 내부 클래스 - 정적 멤버 클래스

static 키워드로 클래스 내부에 선언된 클래스이다. 클래스의 멤버로 모든 종류의 필드와 메서드를 제한 없이 사용할 수 있다.

public class Outer { //외부 클래스 선언
    static int a = 10;
    int b = 20;
    private int c = 30;
    void outerInstanceMethod(){
        System.out.println("외부 클래스의 인스턴스 메서드");
    }
    static void outerStaticMethod(){
        System.out.println("외부 클래스의 정적 메서드");
    }

    static class StaticInner{ //정적 멤버 클래스 선언
        static int d = 40;
        int e = 50;
        private int f = 60;
        //외부 클래스의 모든 필드를 제한 없이 사용 가능하다

        void instanceMethod(){
            System.out.println("정적 멤버 클래스의 인스턴스 메서드");
            Outer outer = new Outer();
            System.out.printf("%d, %d\n", outer.b, outer.c);
            //객체 생성 후 static으로 선언되지 않은 b, c 필드를 사용할 수 있다
            outer.outerInstanceMethod();
            //외부 클래스의 인스턴스 메서드 외부 클래스의 객체 생성 후 사용할 수 있다

        }
        void staticMethod(){
            System.out.println("정적 멤버 클래스의 정적 메서드");
            System.out.printf("%d, %d, %d, %d\n", a, d, e, f);
            //외부 클래스에서 정적으로 선언된 필드는 바로 사용할 수 있다
            Outer.outerStaticMethod();
            //외부 클래스에서 정적으로 선언된 메서드 역시 바로 사용 가능하다          
        }
    }
}

Outer 클래스 외부에서 정적 멤버 클래스의 멤버를 사용하기 위해서는 Outer(외부 클래스)의 객체를 생성해주지 않고 바로 StaticInner(정적 멤버 클래스)의 객체를 생성해서 사용 가능하다.

package innerclass.static_inner.thisisjava;

public class Main {
    public static void main(String[] args) {
        Outer.StaticInner staticInner = new Outer.StaticInner();
        staticInner.instanceMethod();
        staticInner.staticMethod();
    }
}

 

출력 

//staticInner.instanceMethod();
정적 멤버 클래스의 인스턴스 메서드
20, 30
외부 클래스의 인스턴스 메서드
//staticInner.staticMethod();

정적 멤버 클래스의 정적 메서드
10, 40, 50, 60
외부 클래스의 정적 메서드

인스턴스 멤버 클래스 vs. 정적 멤버 클래스


인스턴스 멤버 클래스

정적 멤버 클래스

내부 클래스 자신의 멤버로 static 사용 가능 여부


자신의 멤버로 static을 가질 수 없다

자신의 멤버로 static을 제한없이 사용 가능하다

내부 클래스에서 외부 클래스의 필드에 값을 넣어줄 때


외부 클래스의 필드에 this. 키워드를 붙이지 않고 바로 a = num; 형식으로 사용할 수 있다

this. 키워드를 사용하는 경우는 내부 클래스 자신의 필드에 값을 대입하고자 할 때 사용한다


외부에서 내부 클래스에 접근하고자 하는 경우


외부 클래스의 객체를 먼저 생성하고, 
인스턴스 내부 클래스의 객체를 생성해 사용한다

외부 클래스에서 바로 정적 내부 클래스를 호출하고(.)
객체를 생성해 사용한다

외부 클래스에서 선언된 static 필드와 메서드


객체 생성 없이 호출해 사용 가능하다


외부 클래스에서 선언된 인스턴스 필드와 메서드


객체 생성 없이 바로 불러와 사용할 수 있다

외부 클래스의 객체를 먼저 생성하지 않으면 사용할 수 없다

 


지역 내부 클래스

메서드 내부에서 선언된 클래스를 말한다. 메서드 내부에서만 사용되는 클래스이기 때문에 접근 제한의 의미가 없으므로 접근 제한자는 붙일 수 없고 static 키워드 역시 사용할 수 없다. 즉, 지역 내부 클래스의 멤버로는 인스턴스 필드와 메서드만 선언 가능하다.

public class Outer {
    static int a = 10;
    int b = 20;
    private int c = 30;


    void outerInstanceMethod(){
        System.out.println("외부 클래스의 인스턴스 메서드");

        class LocalInner{
            //static int d = 40; 지역 내부 클래스에서 static 사용 불가
            int e = 50;
            private int f = 60;

            void localInstanceMethod(){
                System.out.println("지역 내부 클래스의 인스턴스 메서드");
                System.out.printf("%d, %d, %d, %d, %d \n", a, b, c, e, f);
            }
            /*
            static localStaticMethod(){
                System.out.println("지역 내부 클래스에서 정적 메서드는 사용 불가");
            }
            */
        }
        LocalInner localInner = new LocalInner();
        //메서드 내에서 지역 내부 클래스의 객체를 생성
        localInner.localInstanceMethod();
        //외부 클래스의 메서드 실행시 내부 클래스의 메서드까지 호출된다
    }

}

 

외부 클래스의 메서드 블록 안에서 지역 내부 클래스의 객체를 생성하고, 메서드를 호출했다. 따라서 외부에서 외부 클래스의 객체를 생성하고 외부 클래스의 메서드를 호출함으로써 지역 내부 클래스의 메서드까지 호출된다.

 

public class Main {
    public static void main(String[] args) {
        Outer outer = new Outer();
        outer.outerInstanceMethod();
    }
}
출력

외부 클래스의 인스턴스 메서드
지역 내부 클래스의 인스턴스 메서드
10, 20, 30, 50, 60

 

+) 비동기 처리를 위해 스레드 객체를 만들 때 주로 사용한다고 한다. 


익명 내부 클래스

익명 내부 클래스는 말 그대로 이름 없이 선언된 클래스이다. 클래스의 선언과 객체의 생성을 동시에 하기 때문에 한 개의 객체만을 생성 가능하며 일회용으로 사용된다.

class Demo {
    void show(){
        System.out.println("Demo class - show()");
    }
}

class Test{
    static Demo d = new Demo(){	//익명 내부 클래스 선언
        void show(){
            super.show();
            System.out.println("Test class - show()");
        }
    };

    public static void main(String[] args) {
        d.show();
    }
}
출력

Demo class - show()
Test class - show()

인스턴스 멤버 클래스, 정적 멤버 클래스, 지역 내부 클래스, 익명 내부 클래스를 알아보았다. 다른 듯 보이지만 모든 내부 클래스는 모두 외부 클래스와 연관되어 있다는 공통점을 가진다. 

 

내부 클래스를 사용함으로써 얻을 수 있는 이점

 

클래스를 논리적으로 그룹화한다

클래스가 여러 클래스와 관계를 맺지 않고 하나의 특정 클래스와만 관계를 맺는다면, 클래스를 새로 작성하는 것이 아닌 내부 클래스로 작성할 수 있다. 이 경우 내부 클래스와 외부 클래스를 함께 관리하는 것이 가능해 편리해질 뿐 아니라, 내부 클래스로 인해 새로운 클래스를 생성하지 않아도 되므로 패키지를 간소화할 수 있다.

 

기존보다 더 많은 캡슐화의 적용이 가능하다

외부 클래스의 멤버를 private으로 선언하면 외부에서는 getter/setter 메서드를 사용하지 않는 한 접근이 불가능하다. 하지만 이 클래스 안에서 내부 클래스를 생성할 경우, 내부 클래스는 private 멤버들에 대해 아무런 제한 없이 접근이 가능하다. 또한, 외부 클래스의 private 멤버를 사용하면서 자기 자신의 접근 제한 역시 private으로 설정해 외부로부터의 접근을 차단할 수 있다.

이것이 내부 클래스로 더 많은 캡슐화의 적용, 즉 외부로부터의 데이터 보호가 가능한 이유이다.

 

가독성이 좋고 유지 관리가 쉬운 코드 작성이 가능하다

내부 클래스를 작성하는 경우, 클래스를 따로 작성하는 경우보다 상위 클래스에 더 가깝게 위치하게 된다. 따라서 시각적으로 읽기가 편해질 뿐 아니라 유지보수에 있어 이점을 가진다.

 


reference.

신용권, 이것이 자바다, 한빛미디어, 2018

 

Nested Classes (The Java™ Tutorials > Learning the Java Language > Classes and Objects)

The Java Tutorials have been written for JDK 8. Examples and practices described in this page don't take advantage of improvements introduced in later releases and might use technology no longer available. See Java Language Changes for a summary of updated

docs.oracle.com

 

What is a Nested Class in Java? | Nested Class Examples

Nested classes help Java developers create more efficient code. Learn how to use nested classes in Java now.

www.developer.com

 

Nested Classes in Java - GeeksforGeeks

A Computer Science portal for geeks. It contains well written, well thought and well explained computer science and programming articles, quizzes and practice/competitive programming/company interview Questions.

www.geeksforgeeks.org

 

private으로 선언된 클래스 내부의 public static 변수 접근

private class Example { public static final PI = 3.14; ... } 위와 같은 코드 구성이 있다면, PI는 외부에서 접근이 가능한가요? 클래서는 private으로 외부로부터의 접근을 제한하는데, PI는 외부의 접근을 허용

hashcode.co.kr