![[JAVA] 중첩 클래스 (중첩 클래스 ,정적 중첩 클래스, 지역 클래스)](https://img1.daumcdn.net/thumb/R750x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc28Dd7%2FbtsHTDnp8Zl%2FcB8jHeH11RsoWlvGTJKQO0%2Fimg.png)
중첩 클래스는 클래스안에 다른 클래스가 중첩되어있음을 의미하는 클래스로 크게는 ===정적 중첩 클래스===, ===내부 클래스===로 나뉘며 내부 클래스에는 다시 내부 클래스, 지역 클래스, 익명 클래스 세 가지로 나뉘어진다
[!중첩클래스와 내부클래스 단어 정리]
중첩 클래스와 내부 클래스의 의미는 면밀히 다르며 중첩 클래스란 중첩 for문과 같이 클래스 안에 다른 클래스가 중첩되어있음 그 자체를 의미하고, 내부 클래스는 클래스안에 내부 클래스가 외부 클래스의 구성요소가 되는 것을 의미한다
- 내부 클래스: 내부 클래스, 지역 클래스, 익명 클래스
- 정적 중첩 클래스: static키워드가 붙은 정적 중첩 클래스
- 중첩 클래스: 정적 중첩 클래스 + 내부 클래스
중첩클래스의 사용 이유
- 논리적 그룹화: 특정 클래스가 다른 하나의 클래스 안에서만 사용되는 경우 두 개의 클래스로 구분하는 것이 아닌, 하나의 클래스안에 포함시키는 것이 논리적으로 더 그룹화 되며 이로인해 다른 곳에서 사용될 필요가 없는 중첩 클래스가 외부에 노출되지 않게 된다
- 캡슐화: 중첩 클래스는 바깥 클래스의 private멤버에 접근할 수 있기때문에 둘을 긴밀하게 연결하여 불필요한 public메서드를 제거할 수 있다. public메서드를 제거한다는 것은 캡슐화가 강화됨을 의미한다
이러한 이유로 중첩 클래스는 서로 긴밀하게 연결되어 있는 특별한 경우에만 사용해야하며 외부의 여러 클래스가 중첩되어있는 클래스를 사용한다면 중첩 클래스로 만들어서는 안된다
정적 중첩 클래스
static nested class
public class NestedOuter {
private static int outClassValue = 3;
private int outInstanceValue = 2;
//정적 중첩 클래스
static class Nested{
private int nestedInstanceValue = 1;
public void print() {
System.out.println(nestedInstanceValue);
System.out.println(outClassValue);
//NestedOuter클래스의 인스턴스 변수이기때문에 접근 불가
//본래 static 클래스에서 인스턴스 클래스에 접근 불가능했음
//System.out.println(outInstanceValue);
}
}
}
public class NestedOuterMain {
public static void main(String[] args) {
NestedOuter.Nested nested = new NestedOuter.Nested();
nested.print();
NestedOuter nestedOuter = new NestedOuter();
}
}
- 정적 중첩 클래스는
new 바깥클래스.중첩클래스()
로 생성할 수 있다 new NestedOuter()
로 생성된 인스턴스와new NestedOuter.Nested()
로 생성된 두 개의 인스턴스는 서로 아무관계가 없는 인스턴스이며, 단지 클래스 구조상 중첩시킴으로 코드의 논리적 그룹화를 위한 중첩이라고 할 수 있다.
class NestedOuter{
}
class Neted{
}
사실상 위 구조와 같다고 할 수 있지만 차이점이 하나 있다면 중첩된 클래스 구조에서는 바깥클래스에서 private으로 선언된 멤버에도 접근할 수 있다는 것이다.
예시
public class Network {
public void sendMessage(String message) {
NetworkMessage networkMessage = new NetworkMessage(message);
networkMessage.print();
}
private static class NetworkMessage{
private String text;
public NetworkMessage(String text) {
this.text = text;
}
public void print() {
System.out.println(text);
}
}
}
public class NetworkMain {
public static void main(String[] args) {
Network network = new Network();
network.sendMessage("hello");
}
}
메인메서드에서는 sendMessage()
메서드를 사용할 때 Network클래스의 인스턴스만 사용하면 메시지를 보내는 기능을 사용할 수 있다.
Network클래스 내부구조를 살펴보면 NetworkMessage라는 정적 중첩 클래스로 선언되어있고 이 클래스는 private키워드를 통해서 외부에서 사용되는 것을 막아두었다.
때문에 코드의 명확성을 줄 수 있다.
[!GPT참고자료]
- 정적 중첩 클래스(static nested class)의 사용:
NetworkMessage
클래스는Network
클래스 내부에 정적(static)으로 선언되어 있기 때문에,Network
클래스의 인스턴스 없이도 존재할 수 있습니다. 하지만 여기서는sendMessage
메서드 내부에서NetworkMessage
의 인스턴스를 생성하여 사용하고 있습니다.- 은닉화(encapsulation)의 적용:
NetworkMessage
클래스는private
접근 제어자를 사용하여 정의되었습니다. 이는Network
클래스 외부에서NetworkMessage
클래스에 접근할 수 없도록 함으로써, 클래스의 내부 구현을 숨기고 사용자에게는sendMessage
메서드를 통해서만 메시지 전송 기능을 제공하고 있습니다. 이는 코드의 명확성과 안전성을 높이는 좋은 방법입니다.- 코드의 명확성:
Network
클래스의 사용자는sendMessage
메서드를 통해 간단하게 메시지를 전송할 수 있습니다. 사용자는NetworkMessage
클래스의 존재나 구현 방법을 알 필요가 없으며,Network
클래스의 API를 통해서만 작업을 수행할 수 있습니다. 이렇게 클래스의 내부 구현을 숨기고 사용자에게 필요한 기능만을 제공하는 것은 객체지향 프로그래밍에서 중요한 원칙 중 하나입니다.요약하자면, 여러분의 코드와 설명은 Java에서 정적 중첩 클래스(static nested class)와 은닉화(encapsulation) 원칙을 활용하여 코드의 명확성을 높이는 방법을 잘 보여주고 있습니다.
내부 클래스
내부 클래스는 정적중첩 클래스와는 다르게 바깥클래스의 인스턴스를 이루는 요소로 들어간다
즉, 내부 클래스는 바깥 클래스의 인스턴스에 소속된다.
public class InnerOuter {
private static int outClassValue = 3;
private int outInstanceValue = 2;
class Inner{
private int innerInstanceValue = 1;
public void print() {
System.out.println(innerInstanceValue);
System.out.println(outClassValue);
System.out.println(outInstanceValue);
}
}
}
public class InnerOuterMain {
public static void main(String[] args) {
InnerOuter outer = new InnerOuter();
InnerOuter.Inner inner = outer.new Inner();
inner.print();
}
}
내부 클래스의 인스턴스는 외부클래스의 인스턴스 멤버가 되기때문에 내부 클래스의 인스턴스를 생성하기위해서는 외부 클래스 인스턴스의 참조변수를 참조해야한다.outer.new Inner();
이 때 생성하는 인스턴스의 타입은 InnerOuter.Inner
로 받아주면 된다.
예시
public class CarMain {
public static void main(String[] args) {
Car myCar = new Car("Model Y,",100);
myCar.start();
}
}
public class Engine {
private Car car;
public Engine(Car car) {
this.car = car;
}
public void start() {
System.out.println("충전 레벨 확인: " + car.getChargeLevel());
System.out.println(car.getModel() + "의 엔진을 구동합니다");
}
}
public class Car {
private String model;
private int chargeLevel;
private Engine engine;
public Car(String model, int chargeLevel) {
this.model = model;
this.chargeLevel = chargeLevel;
this.engine = new Engine(this);
}
public String getModel() {
return model;
}
public int getChargeLevel() {
return chargeLevel;
}
public void start() {
engine.start();
System.out.println(model + "시작 완료");
}
}
Engine - Car 사이에는 긴밀한 관계를 가지고있으며 Car클래스의 getModel()
, getChargeLevel()
메서드들은 외부에 노출될 필요없이 오로지 Engine클래스에서만 사용되며 Engine의 정보를 얻기위한 메서드이다.
이러한 경우에는 내부 클래스로 Engine클래스를 만들어 캡슐화를 명확하게하고, 코드의 가독성또한 향상시킬 수 있다.
내부 클래스로 리팩토링
public class Car {
private String model;
private int chargeLevel;
private Engine engine;
public Car(String model, int chargeLevel) {
this.model = model;
this.chargeLevel = chargeLevel;
this.engine = new Engine();
}
public void start() {
engine.start();
System.out.println(model + "시작 완료");
}
private class Engine {
public void start() {
System.out.println("충전 레벨 확인: " + chargeLevel);
System.out.println(model + "의 엔진을 구동합니다.");
}
}
}
Engine을 Car클래스의 내부 클래스로 리팩토링하고, Engine클래스를 private으로 선언해줌으로써 Car클래스 외의 외부클래스에서는 Engine에 관련된 정보에는 접근할 수 없게된다. 이로인해 캡슐화를 극대화할 수 있다.
[!참고]
만약private class Engine
을public class Engine
으로 바꾸게 되면Car.Engine engine = myCar.new Engine();
이 코드를 통해 내부 클래스의 인스턴스를 생성할 수 있다.
내부 클래스만으로 생성된 engine변수는 외부 클래스에 있는 정보는 알지 못하기때문에 외부 클래스의 메서드, 필드에는 접근할 수 없다.
지역 클래스
public class LocalOuterV1 {
private int outInstanceVar = 3;
public void process(int paramVar) {
int localVar = 1;
class LocalPrinter{
int value = 0;
public void printData() {
System.out.println("value = " + value);
System.out.println("localVar = " + localVar);
System.out.println("paramVar = " + paramVar);
System.out.println("outInstanceVar = " + outInstanceVar);
}
}
LocalPrinter printer = new LocalPrinter();
printer.printData();
}
public static void main(String[] args) {
LocalOuterV1 localOuter = new LocalOuterV1();
localOuter.process(1);
}
}
지역 클래스는 지역변수와 같은 개념으로 주로 메서드 내부에서 선언되는 내부 클래스를 의미한다.
지역 클래스도 내부 클래스이므로 외부 클래스의 멤버변수에 접근할 수 있다.
요약
정적 중첩 클래스
바깥 클래스와 밀접한 관련이 있지만, 인스턴스 간에 데이터 공유가 필요 없을 때 사용한다
내부 클래스
바깥 클래스의 인스턴스와 연결되어 있고 바깥 클래스의 인스턴스 상태에 의존하거나 강하게 연관된 작업을 수행할 때 사용한다
지역 클래스
- 내부 클래스의 특징을 가진다
- 지역 변수에 접근할 수 있다 접근하는 지역 변수는 final이거나 사실상 final이어야한다
- 주로 특정 메서드 내에서만 간단히 사용할 목적으로 사용한다
익명 클래스
- 지역 클래스이지만 이름이 없다
- 상위 타입을 상속 또는 구현하면서 바로 생성된다
- 주로 특정 상위 타입을 간단히 구현해서 일회성으로 사용할 때 유용하다
공부자료 참조: 인프런 - 김영한
https://www.inflearn.com/users/74366/@yh
'Language > Java' 카테고리의 다른 글
[JAVA] 날짜와 시간 - LocalDate, LocalTime (0) | 2024.06.09 |
---|---|
[JAVA - 자료구조] ArrayList (1) | 2024.06.07 |
[JAVA] 다형성 (0) | 2024.06.02 |
[Java] - 배열(Array) (0) | 2023.10.23 |
[Java] - BufferedWriter (0) | 2023.10.18 |
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!