본문으로 바로가기

[Java] 중첩 클래스

category Backend/Java 2022. 4. 26. 14:54

1. 중첩 클래스란?

클래스 내부에 선언한 클래스

public class 클래스 {
    class 중첩클래스 {
        
    }
}

 

중첩 클래스를 쓰는 이유

클래스 관계

객체 지향 프로그램에서 클래스들은 서로 긴밀한 관계를 맺고 상호작용을 합니다.

어떤 클래스는 여러 클래스와 관계를 맺지만 어떤 클래스는 특정 클래스와 관계를 맺습니다.

 

클래스가 여러 클래스와 관계를 맺는 경우에는 독립적으로 선언하는 것이 좋으나, 

특정 클래스와 관계를 맺을 경우에는 관계 클래스를 클래스 내부에 선언하는 것이 좋습니다.

 

장점

1) 중첩 클래스를 사용하면 두 클래스의 멤버들을 서로 쉽게 접근할 수 있습니다.

2) 외부에 불필요한 관계 클래스를 감춤으로써 코드의 복잡성을 줄일  있습니다.

 

2. 중첩 클래스

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

1) 클래스의 멤버로서 선언되는 클래스(멤버 클래스)

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

 

2) 메소드 내부에서 선언되는 클래스(로컬 클래스)

 

인스턴스 멤버 클래스

class A {
	class B { ... } // A 객체를 생성해야만 사용할 수 있는 B 중첩 클래스
}
  • static 키워드 없이 선언된 클래스
  • 인스턴스 멤버 클래스는 인스턴스 필드와 메소드만 선언이 가능하고 정적 필드와 메소드는 선언할 수 없습니다.
  • 인스턴스 멤버 클래스의 객체를 생성하려면 먼저 바깥 클래스의 객체를 생성하고 인스턴스 멤버 클래스의 객체를 생성해야 합니다.

 

정적 멤버 클래스

class A {
	static class B { ... } // A 클래스로 바로 접근 할 수 있는 B 중첩 클래스
}
  • static 키워드로 선언된 클래스
  • 정적 멤버 클래스는 모든 종류의 필드와 메소드를 선언할 수 있습니다.
  • 정적 멤버 클래스의 객체를 생성하려면 바깥 클래스의 객체를 생성할 필요가 없고 바로 생성할 수 있습니다.

 

로컬 클래스

class A {
	void method() {
		class B { ... } // method()가 실행될 때만 사용할 수 있는 B 중첩 클래스
	}
}
  • 메소드 내에 선언한 중첩 클래스
  • 로컬 클래스는 접근 제한자(public, private) 및 static을 붙일 수 없습니다.
  • 로컬 클래스 내부에는 인스턴스 필드와 메소드만 선언이 가능하고 정적 필드와 정적 메소드는 선언할 수 없습니다.
  • 로컬 클래스는 메소드가 실행될 때 메소드 내에서 객체를 생성하고 사용해야 합니다.
  • 주로 비동기 처리를 위해 스레드 객체를 만들 때 사용합니다.

 

3. 인스턴스 멤버 클래스

public class A {
    //==인스턴스 멤버 클래스==//
    public class B {
        private String name;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }
    }
}
A a = new A();
A.B b = a.new B(); // 인스턴스 멤버 객체 생성
b.setName("veneas");
System.out.println(b + " : " + b.getName()); // A$B@396a51ab : veneas

 

4. 정적 멤버 클래스

public class A {
    //==정적 멤버 클래스==//
    public static class C {
        private String name;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }
    }
}
A.C c = new A.C(); // 정적 멤버 객체 생성
c.setName("veneas");
System.out.println(c + " : " + c.getName()); // A$C@5034c75a : veneas

 

5. 로컬 클래스

public class A {
    public void createClassD() {
        //==로컬 클래스==//
        class D {
            private String name;

            public String getName() {
                return name;
            }

            public void setName(String name) {
                this.name = name;
            }
        }

        D d = new D();
        d.setName("D class");
        System.out.println(d + " : " + d.getName());
    }
}
A a = new A();
a.createClassD(); //A$1D@396a51ab : D class

 

6. 중첩 클래스 사용 예시 (Spring Framework)

API를 개발할 경우 규격에 맞추는 것이 중요한 데 요청 규격, 응답 규격을 맞출 때 DTO를 작성을 하게 되는데 그 경우 중첩 클래스로 작성을 하는 것을 권장합니다.

 

예시(응답 규격 DTO)

import lombok.AllArgsConstructor;
import lombok.Data;
import org.springframework.web.bind.annotation.*;

import java.util.ArrayList;
import java.util.List;

@RestController
public class TestController {

    @GetMapping(value = "/stock/info")
    public Response info() {
        return new Response(200, "OK", new ResponseStockInfo("Apple Inc","AAPL","NASDAQ", 167.30));
    }

    @GetMapping(value = "/stock/list")
    public Response list() {
        List<ResponseStockList> stockLists =  new ArrayList<>();
        stockLists.add(new ResponseStockList("Apple Inc", "AAPL"));
        stockLists.add(new ResponseStockList("Microsoft Corporation", "MSFT"));

        return new Response(200, "OK", stockLists);
    }

    @GetMapping(value = "/stock/union")
    public ResponseUnion union() {
        ResponseStockInfo responseStockInfo = new ResponseStockInfo("Apple Inc","AAPL","NASDAQ", 167.30);

        List<ResponseStockList> stockLists =  new ArrayList<>();
        stockLists.add(new ResponseStockList("Apple Inc", "AAPL"));
        stockLists.add(new ResponseStockList("Microsoft Corporation", "MSFT"));

        return new ResponseUnion(200, "OK", responseStockInfo, stockLists);
    }

    @GetMapping("/favicon.ico")
    public void favicon() { }

    //==Response DTO==//
    @Data
    @AllArgsConstructor
    static class Response<T> {
        private Integer code;
        private String msg;
        private T data;
    }

    //==Response DTO==//
    @Data
    @AllArgsConstructor
    static class ResponseStockInfo {
        private String companyName;
        private String ticker;
        private String market;
        private Double price;
    }

    //==Response DTO==//
    @Data
    @AllArgsConstructor
    static class ResponseStockList {
        private String companyName;
        private String ticker;
    }

    //==Response DTO==//
    @Data
    @AllArgsConstructor
    static class ResponseUnion<T, V> {
        private Integer code;
        private String msg;
        private T data1;
        private V data2;
    }
}

/stock/info
/stock/list
/stock/union