[JAVA] 중첩(내부) 클래스

종원유·2022년 2월 15일
0

Java

목록 보기
6/11
post-thumbnail

사진참조

이번 포스트는 중첩 클래스에 대해 정리하고자 한다.

먼저 중첩 클래스는 두가지로 나눌 수 있다.

  • 스태틱 클래스
  • 내부 클래스

중첩 클래스의 사용 이유는 클래스들의 논리적인 그룹을 model 내 객체에서 나타낼 때 사용한다.
중첩 클래스를 그룹 내부에 선언하기 때문에 좋은 가독성과 유지보수성을 가진다.
더 향상된 캡슐화의 형태이다.

스태틱 클래스

class OutderClass{
    @Builder
    @ToString
    static class User{
        public String name;

        public String mail;
    }
}

public class StaticClass {
    public void test(){
    //외부에서 내부 static 중첩 클래스 선언
        OuterClass.User user = new OuterClass.User.UserBuilder().name("test2").mail("google.com").build();
    }
}

정적 중첩클래스(스태틱 클래스)는 내부 클래스와 구조가 굉장히 비슷하지만,
다음과 같은 차이점이 존재한다.

  • 내부 클래스의 경우는 외부 클래스의 자원을 자유롭게 사용하지만, 중첩 정적 클래스의 경우 static 키워드가 붙은 자원에만 접근 가능하다.
  • Outer클래스가 존재하지 않아도, Inner 클래스의 생성이 가능하다.

위 코드의 StaticClass의 test()메서드를 보면 정적 중첩클래스를 외부에서 생성하는 방법이다.

내부 클래스

내부 클래스는 아래와 같이 3가지로 나눌 수 있다.

  • 내부 클래스
  • 로컬 클래스
  • 익명 내부 클래스

내부 클래스

public class OuterClass {

    private String outer1;

    class InnerClass{
        public void test(){
            System.out.println(outer1);
            System.out.println("내부 클래스 호출입니다.");
        }

    }
        public static void main(String[] args) {
        OuterClass outerClass = new OuterClass();
        outerClass.outer1 = "외부 클래스 변수1"; //외부 클래스 멤버변수 초기화
        OuterClass.InnerClass innerClass = outerClass.new InnerClass(); //내부 클래스를 외부에서 객체 생성 
        innerClass.test();
    }
}

출력결과

외부 클래스 변수1
내부 클래스 호출입니다.

내부 클래스는 내부 클래스를 감싸고 있는 외부 클래스의 멤버들을 자유롭게 접근할 수 있다.
단일 클래스 내에서만 사용되는 클래스의 경우는 내부 클래스로 선언하여 사용하는 것이 좋다.
그 이유는

  • 내부 클래스를 사용하여 구조를 구성할 경우 코드의 복잡성을 줄일 수 있다.
  • 계속 늘어나는 클래스를 내부 클래스로 선언하여 유지보수성과 가독성을 좋게 만들 수 있고, 클래스 파일을 늘리는 번거로운 작업을 줄일 수 있다.

내부 클래스를 외부에서 생성하는 방법은 위 코드를 참고.

로컬 클래스

public class OuterClass {
	    public void method(final User user){
        	class LocalClass{
            public void localClassTest(){
                System.out.println("로컬 클래스" + user.toString());
            }
        }
        new LocalClass().localClassTest();
        }
	public static void main(String[] args) {
    	 OuterClass outerClass = new OuterClass();
        User user = new User.UserBuilder().name("test").mail("naver.com").build();
        outerClass.method(user);
    }
}

출력결과

로컬 클래스OuterClass.User(name=test, mail=naver.com)
내부 익명 클래스OuterClass.User(name=test, mail=naver.com)

로컬 클래스는 메서드 안에 선언된 클래스로 메서드 안에서만 사용되는 클래스는 위와 같이 선언하여 사용할 수 있다.
로컬 클래스의 장점은 메서드 파라미터 및 지역 변수에 자유롭게 접근이 가능하다.

익명 내부 클래스

public class OuterClass {
	    public void method(final User user){
        TestInterface testInterface = new TestInterface(){
            @Override
            public void implementsMethod() {
                System.out.println("내부 익명 클래스" + user.toString());
            }
        };
        
        //람다식 표기 
        //TestInterface testInterface = () -> System.out.println("내부 익명 클래스" + user.toString());
        
        testInterface.implementsMethod();
        }
	public static void main(String[] args) {
    	 OuterClass outerClass = new OuterClass();
        User user2 = User.builder().name("test2").mail("google.com").build();
        outerClass.method(user2);
    }
}

출력결과

로컬 클래스OuterClass.User(name=test2, mail=google.com)
내부 익명 클래스OuterClass.User(name=test2, mail=google.com)

마지막으로 익명 내부 클래스는 타입이나 이름이 없기 때문에, 상속이나 구현을 할 때, 인터페이스나 추상 클래스의 타입으로 선언하여 사용한다.

익명 클래슨느 인스턴스 이름이 없기 때문에, new와 동시에 부모 클래스를 상속받아 내부에서 오버라이딩하여 사용.

익명 내부 클래스의 변수나 메서드는 익명 클래스 내부에서만 사용이 가능하다.
또, 익명 클래스에서 외부 자원을 사용할 때, 사용할 자원은 반드시 final로 선언되어야만 한다.

익명 클래스 선언은 TestInterface testInterface = () -> System.out.println("내부 익명 클래스" + user.toString()); 처럼 람다식 표기로 사용할 수도 있다.

전체코드


public class StaticClass {
    public void test(){
        //외부에서 내부 static 중첩 클래스 선언
        OuterClass.User user = new OuterClass.User.UserBuilder().name("test2").mail("google.com").build();
    }
}


public class OuterClass {

    private String outer1;

    @Builder
    @ToString
    static class User{
        public String name;

        public String mail;
    }

    class InnerClass{
        public void test(){
            System.out.println(outer1);
            System.out.println("내부 클래스 호출입니다.");
        }

    }

    public void method(final User user){
        class LocalClass{
            public void localClassTest(){
                System.out.println("로컬 클래스" + user.toString());
            }
        }
        new LocalClass().localClassTest();
        TestInterface testInterface = new TestInterface(){
            @Override
            public void implementsMethod() {
                System.out.println("내부 익명 클래스" + user.toString());
            }
        };
        testInterface.implementsMethod();
    }

    public interface TestInterface{
        void implementsMethod();
    }

    public static void main(String[] args) {
        OuterClass outerClass = new OuterClass();
        outerClass.outer1 = "외부 클래스 변수1";
        //내부 클래스
        OuterClass.InnerClass innerClass = outerClass.new InnerClass();
        innerClass.test();

        User user = new User.UserBuilder().name("test").mail("naver.com").build();
        outerClass.method(user);

        User user2 = User.builder().name("test2").mail("google.com").build();
        outerClass.method(user2);
    }

}
profile
개발자 호소인

0개의 댓글