[JAVA] 중첩 클래스. 내부 클래스3

2025. 7. 9. 19:17·자바

중첩 클래스, 내부 클래스 2에서는 지역클래스를 알아봤다면 이번에는 익명 클래스에 대해서 알아볼 것이다.

 

자바에서 익명 클래스(Anonymous Class)는 이름이 없는 클래스이다. 특정 클래스나 인터페이스를 상속 또는 구현하면서 동시에 인스턴스를 생성할 때 사용한다. 주로 메서드 내에서 일회성으로 사용되며, 별도의 클래스를 정의하지 않아도 되는 장점이 있다.

 

문법

타입 변수 = new 부모클래스() {
    // 메서드 오버라이딩
};

또는

타입 변수 = new 인터페이스() {
    // 메서드 구현
};

 

익명 클래스는 클래스의 본문을 {} 안에 정의한다. 이 본문은 상속하거나 구현한 대상의 메서드를 오버라이딩한 내용이다. 동시에 new 키워드를 사용하여 인스턴스를 생성한다.

 

예시

인터페이스 Printer가 다음과 같이 정의되어 있다고 가정해보자.

public interface Printer {
    void print();
}

이를 익명 클래스로 구현하면 다음과 같다.

Printer printer = new Printer() {
    @Override
    public void print() {
        System.out.println("출력합니다.");
    }
};

위 코드는 Printer 인터페이스를 구현하는 이름 없는 클래스를 정의하고, 동시에 인스턴스를 생성하여 printer 변수에 할당한다.

 

이제부터 익명클래스가 왜 필요할지 코드를 보면서 알아보자.

 

 

1. 중복된 출력 메서드의 리팩토링

다음 코드는 helloJava()와 helloSpring()이라는 두 메서드를 통해 출력만 다를 뿐 구조가 동일한 작업을 수행한다.

리펙토링 전:

public static void helloJava() {
    System.out.println("프로그램 시작");
    System.out.println("Hello Java");
    System.out.println("프로그램 종료");
}

public static void helloSpring() {
    System.out.println("프로그램 시작");
    System.out.println("Hello Spring");
    System.out.println("프로그램 종료");
}

위와 같은 중복은 유지보수에 불리하다. 출력 메시지를 매개변수로 받아 처리하면 다음과 같이 단일 메서드로 통합할 수 있다.

 

리펙토링 후:

public static void hello(String msg) {
    System.out.println("프로그램 시작");
    System.out.println(msg);
    System.out.println("프로그램 종료");
}

 

문자열이라는 변하는 요소를 외부에서 주입하고, 나머지 변하지 않는 틀을 재사용하도록 구성한 것이다. 이는 중복 제거의 첫걸음이 된다.

 

 

2. 코드 조각 전달의 필요성

앞에서는 문자열을 전달했지만, 다음 예제에서는 메시지가 아니라 로직 전체가 달라지는 경우이다.

 

리펙토링 전:

public static void helloDice() {
    System.out.println("프로그램 시작");
    int randomValue = new Random().nextInt(6) + 1;
    System.out.println("주사위 = " + randomValue);
    System.out.println("프로그램 종료");
}

public static void helloSum() {
    System.out.println("프로그램 시작");
    for (int i = 1; i <= 3; i++) {
        System.out.println("i = " + i);
    }
    System.out.println("프로그램 종료");
}

 

이 경우에는 단순히 데이터를 전달하는 것이 아니라, 실행할 코드 조각 자체를 전달해야 한다. 자바는 메서드를 직접 전달할 수 없으므로, 인터페이스와 다형성을 활용한다.

 

 

3. 인터페이스 도입과 클래스 분리

코드 조각을 감싸는 인터페이스를 정의하고, 각각의 구현 클래스를 작성한다.

 

1번 리펙토링 후:

 public static void hello(Process process) {
 	System.out.println("프로그램 시작");
 	//코드 조각 시작
    process.run();
 	//코드 조각 종료
	System.out.println("프로그램 종료");
}
public interface Process {
    void run();
}

static class Dice implements Process {
    @Override
    public void run() {
        int randomValue = new Random().nextInt(6) + 1;
        System.out.println("주사위 = " + randomValue);
    }
}

static class Sum implements Process {
    @Override
    public void run() {
        for (int i = 1; i <= 3; i++) {
            System.out.println("i = " + i);
        }
    }
}
 public static void main(String[] args) {
     Process dice = new Dice();
     Process sum = new Sum();
     System.out.println("Hello 실행");
     hello(dice);
     hello(sum);
}

 

 

즉 다형성을 사용해서 해당 인스턴스에 따라 코드 조각이 실행 된다. 이는 유지 보수 측면에도 용이한 것을 볼 수 있다.

 

 

4. 지역 클래스의 사용

정적 클래스를 따로 정의하지 않고, 메서드 안에 지역 클래스로 작성할 수도 있다. 이 방식은 외부에서 재사용할 필요 없이, 해당 메서드 내부에서만 일회성으로 사용할 때 적합하다.

 

2번 리펙토링 후:

 public static void hello(Process process) {
 	System.out.println("프로그램 시작");
 	//코드 조각 시작
    process.run();
 	//코드 조각 종료
	System.out.println("프로그램 종료");
  }
public static void main(String[] args) {
    class Dice implements Process {
        public void run() {
            System.out.println("주사위 = " + new Random().nextInt(6) + 1);
        }
    }
    class Sum implements Process {
        public void run() {
            for (int i = 1; i <= 3; i++) System.out.println("i = " + i);
        }
    }

    hello(new Dice());
    hello(new Sum());
}

 

 

5. 익명 클래스 도입

개발자들의 욕심은 끝이 없다 따라서 한번 더 리펙토링 을 하기위해 익명 클래스를 사용할 수 있다. 이름 없는 클래스를 정의하면서 동시에 인스턴스를 생성한다.

 

3번 리펙토링 후:

 public static void hello(Process process) {
 	System.out.println("프로그램 시작");
 	//코드 조각 시작
    process.run();
 	//코드 조각 종료
	System.out.println("프로그램 종료");
  }
 public static void main(String[] args) {
        Process dice = new Process() {
            @Override
            public void run() {
                int randomValue = new Random().nextInt(6) + 1;
                System.out.println("주사위 = " + randomValue);
            }
        };

        Process sum = new Process() {
            @Override
            public void run() {
                for (int i = 1; i <= 3; i++) {
                    System.out.println("i = " + i);
                }
            }
        };

        System.out.println("Hello 실행");
        hello(dice);
        hello(sum);
    }
}

 

익명 클래스는 클래스 이름이 없으므로 재사용은 불가능하지만, 단 한 번만 사용할 객체라면 코드가 매우 간결해진다.

 

 

6. 람다 표현식으로 간결화

자바 8부터는 인터페이스에 메서드가 하나뿐일 경우, 익명 클래스조차 생략하고 람다(Lambda) 문법으로 대체할 수 있다. Process 인터페이스는 void run() 하나만 가지고 있으므로 람다 대상이다.

 

최종 리펙토링:

 public static void hello(Process process) {
 	System.out.println("프로그램 시작");
 	//코드 조각 시작
    process.run();
 	//코드 조각 종료
	System.out.println("프로그램 종료");
  }
public static void main(String[] args) {
    hello(() -> {
        int randomValue = new Random().nextInt(6) + 1;
        System.out.println("주사위 = " + randomValue);
    });

    hello(() -> {
        for (int i = 1; i <= 3; i++) {
            System.out.println("i = " + i);
        }
    });
}

 

 

마무리하며

지금까지 코드를 리팩토링해 나가며 중복 제거 → 코드 조각 전달 → 인터페이스 도입 → 지역 클래스 → 익명 클래스 → 람다 표현식으로 발전하는 과정을 단계적으로 살펴보았다.

 

익명 클래스는 클래스를 정의하지 않고도 일회성 로직을 간결하게 구현할 수 있는 방법이다. 특히 콜백, 이벤트 처리, 전략 패턴 등에서 자주 사용된다. 이후 자바 8의 등장으로 람다가 도입되면서 함수형 인터페이스를 구현하는 코드는 더욱 간단하고 읽기 쉬워졌다.

 

익명 클래스는 지금도 여전히 유효한 문법이며, 람다로 대체되지 않는 상황(예: 두 개 이상의 메서드를 가진 인터페이스 구현, 생성자 필요 등)에서는 여전히 유용하게 쓰인다.

 

중요한 것은 상황에 맞는 선택이다.
익명 클래스를 이해하고 활용할 수 있다면, 더 나은 리팩토링과 유연한 코드 구성이 가능해진다.

감사합니다.

'자바' 카테고리의 다른 글

[JAVA] 예외 처리 2 -이론  (5) 2025.07.10
[JAVA] 예외 처리1 -이론  (0) 2025.07.10
[JAVA] 중첩 클래스, 내부 클래스 2  (2) 2025.07.09
[JAVA] 중첩 클래스, 내부 클래스 1  (2) 2025.07.07
[JAVA] 날짜와 시간  (1) 2025.07.06
'자바' 카테고리의 다른 글
  • [JAVA] 예외 처리 2 -이론
  • [JAVA] 예외 처리1 -이론
  • [JAVA] 중첩 클래스, 내부 클래스 2
  • [JAVA] 중첩 클래스, 내부 클래스 1
0kingki_
0kingki_
자바 + 스프링 웹 개발
  • 0kingki_
    0kingki_
    0kingki_
  • 전체
    오늘
    어제
    • 분류 전체보기 (134)
      • 코딩 테스트 (54)
      • 자바 (21)
      • 스프링 (27)
      • 타임리프 (16)
      • 스프링 데이터 JPA (8)
      • 최적화 (2)
      • QueryDSL (4)
      • AWS (2)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    불변객체
    스프링
    재귀
    스프링 컨테이너
    fetch join
    QueryDSL
    Java
    객체지향
    SpringDataJpa
    쿼리
    컬렉션
    백준
    예외처리
    다형성
    thymeleaf
    mvc
    dfs
    SOLID
    코딩테스트
    BFS
    LocalDateTime
    쿼리dsl
    자바
    스프링 데이터 JPA
    예외 처리
    코딩 테스트
    JPA
    spring
    타임리프
    최적화
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.4
0kingki_
[JAVA] 중첩 클래스. 내부 클래스3
상단으로

티스토리툴바