Java & Kotlin

[Java] 익명 객체(Anonymous Object)를 통해 인터페이스와 추상 클래스의 객체를 바로 생성하기

망나니개발자 2022. 4. 11. 10:00
반응형

Spring이 제공하는 유틸성 클래스는 모두 abstract로 선언되어 있음을 볼 수 있습니다. 아마 객체의 생성을 방지하기 위한 의도였을 것 같은데, 익명 객체(Anonymous Object)를 통해 인터페이스와 추상 클래스의 객체를 바로 생성하는 방법을 살펴보도록 하겠습니다.

 

 

 

1. 익명 객체(Anonymous Object)를 통해
인터페이스와 추상 클래스의 객체를 바로 생성하기


[ 인터페이스와 추상 클래스의 객체 생성 불가능 ]

예를 들어 다음과 같은 Animal 추상 클래스가 있다고 가정을 하자.

public abstract class Animal {
    
    abstract void bark();
    
}

 

 

우리는 추상클래스에 대해 평소처럼 객체를 생성하려고 하면 에러가 발생하게 된다. 에러 내용을 분석해보면 Animal이 추상클래스이기 때문에 객체로 인스턴스화 할 수 없다는 내용이다.

 

 

자바에서 추상 클래스나 인터페이스를 객체로 만들 수 없게 해둔 이유는 메소드의 구현체가 존재하지 않기 때문이다. 그렇다면 추상 클래스나 인터페이스를 객체로 만드는 동시에 메소드를 구현하면 어떨까? 이에 대해 살펴보도록 하자.

 

 

 

 

[ 익명 객체(Anonymous Object)로 객체 생성하기 ]

자바에서는 일시적으로 한번만 사용이 필요할 때 사용하도록 익명 객체를 제공하고 있다. 익명 객체는 말 그대로 이름이 없는 객체를 의미하며, 일반적으로 생성한 후에 바로 다른 메소드나 클래스의 파라미터로 넘겨줄 때 사용된다.

추상 클래스나 인터페이스의 경우에도 익명 객체를 생성하면서 동시에 메소드를 오버라이딩해준다면 객체로 만들 수 있다. 다음과 같이 위의 Animal 클래스의 익명 객체를 만들수 있다.

@Test
void barkTest() {
    Animal anonymousObject = new Animal() {

        @Override
        public void bark() {
            System.out.println("bark");
        }
    };

    anonymousObject.bark();
    System.out.println(anonymousObject.getClass());
}

 

 

위와 같이 추상 클래스나 인터페이스를 익명 객체로 생성가능한 이유는 객체를 생성하면서 메소드를 오버라이딩 해주었기 때문이다.

그렇다면 익명 객체의 클래스는 어떻게 될까? 위에서 출력한 익명 객체의 클래스 정보를 출력해보면 다음과 같이 출력이 된다.

 

 

일반적으로 클래스 정보에서 $는 내부 클래스 또는 이너 클래스를 의미한다. 즉, 위와 같이 객체를 생성할 때 선언했던 내용들은 해당 클래스의 내부 클래스에 생성되며, 내부 클래스는 이름이 없으므로 임의로 1, 2, 3 ... N 순서대로 값이 부여되는 것이다.

익명 객체를 만드는 작업은 해당 클래스의 이너 클래스를 생성하는 것이다. 하지만 이러한 방식은 결국 지역 클래스(Local Class)를 생성해서 사용하는 방법 중 하나에 불과하다. 위의 코드를 풀어서 적으면 다음과 같다.

@Test
void barkTest() {
    class MyAnimal extends Animal {

        @Override
        public void bark() {
            System.out.println("bark");
        }
    };
    
    Animal anonymousObject = new MyAnimal();

    anonymousObject.bark();
    System.out.println(anonymousObject.getClass());
}

 

 

 

익명 클래스는 지역 클래스(Local Class)의 한 종류 중 하나로, 클래스의 이름이 없다는 특징이 없다. 익명 클래스를 사용하면 지역 클래스의 선언과 생성을 한 번에 할 수 있다는 점에서 코드가 간결해진다.

하지만 복잡하거나 재사용이 필요한 경우에는 별도의 클래스를 정의하는 것이 좋다. 왜냐하면 익명 클래스는 선언과 생성을 한번에 하는 것이므로 단 한번만 인스턴스를 생성할 수 있기 때문이다.

 

 

스프링에서는 이러한 익명 객체 방식이 많이 사용되어 이러한 구조를 템플릿 메소드 패턴이라고도 하는데, JdbcTemplate과 같이 *Template으로 선언된 클래스들은 모두 이러한 익명 객체를 기반으로하는 템플릿 메소드 패턴이 적용되었다고 이해하면 된다. 참고로 템플릿 메소드 패턴은 GoF 디자인 패턴이 아니고, 스프링에서 자주 사용되어 스프링에서만 불리는 디자인 패턴 이름이다.

 

 

 

 

 

 

반응형