본문 바로가기
개발 관련 지식/JAVA

익명 객체

by 권태일1147 2020. 4. 8.

익명 객체는 이름이 없는 객체를 말한다. 익명 객체는 단독으로 생성할 수 없다. 클래스를 상속하거나 인터페이스를 구현해야만 생성할 수 있다. 익명 객체는 필드의 초기값이나 로컬변수의 초기값, 매개 변수의 매개값으로 주로 대입된다.

 

익명 자식 객체 생성

우선 부모 클래스를 자식 클래스에 상속하고 선언한 후, new 연산자를 이용해서 자식 객체를 생성해서 필드나 로컬 변수에 대입하는 것이 기본이다.

class Child extends Parent { }

class A {
    Parent p = new Child();
    void method() {
        Parent localVar = new Child();
    }
}

하지만 자식 클래스가 재사용되지 않고, 오로지 해당 필드와 변수의 초기값으로만 사용할 경우라면 익명 자식 객체를 생성해서 초기값으로 대입하는 것이 좋은 방법이다. 익명 자식 객체는 실행문이기 때문에 마지막에 세미콜론 ; 을 반드시 붙여야 한다.

Parent fieldOVar = new Parent(...) {
    // 필드
    // 메소드
};

Parent(... ) {} 는 부모 클래스를 상속해서 중괄호 {} 처럼 자식 클래스를 선언하라는 뜻이고, new 연산자는 이렇게 선언된 자식 클래스를 객체로 생성한다.

 

익명 자식 객체에 새롭게 정의된 필드와 메소드는 익명 자식 객체 내부에서만 사용되고, 외부에서는 필드와 메소드에 접근할 수 없다. 왜냐하면 익명 자식 객체는 부모 타입 변수에 대입되기 때문에 부모 타입에 선언된 것만 사용할 수 있다. 자동 타입 변환 되었을 때와 마찬가지이다. 자식 객체를 참조하지만 타입은 부모 타입이기 때문에 부모의 멤버만 사용이 가능한 것이다.

class A {
    Parent p = new Parent() {
        int childField;
        void childMethod() { }
        @Override
        void parentMethod() {
            childField = 3; // 익명 자식 객체 내부에서는 호출 가능
            childMethod();  // 익명 자식 객체 내부에서는 호출 가능
        }
    }
    
    void method() {
        p.childField = 3; // 사용 불가. p는 부모 타입이므로 부모 객체에 선언된 것만 사용가능하다.
        p.childMethod();  // 사용 불가. p는 부모 타입이므로 부모 객체에 선언된 것만 사용가능하다.
        p.parentMethod(); // 사용 가능.
    }
}

 

 

익명 구현 객체 생성

일반적으로 구현 객체를 생성하는 방법은 구현 클래스를 선언하고, new 연산자를 이용해서 구현 객체를 생성한 후, 필드나 로컬 변수에 대입하는 것이 기본이다.

class TV implements RemoteControl { }

class A {
    RemoteControl field = new TV(); // 필드에 구현 객체 대입
    void method() {
        RemoteControl localVar = new TV(); // 로컬 변수에 구현 객체 대입
    }
}

그러나 구현 클래스가 재사용되지 않고, 오로지 해당 필드와 변수의 초기값으로만 사용하는 경우라면 익명 구현 객체를 초기값으로 대입하는 것이 좋다.

Interface fieldOrVar = new Interface() {
    // 인터페이스의 추상 메소드를 구현하는 실체 메소드 선언
    // 필드
    // 메소드
}

중괄호 { } 처럼 클래스를 선언하라는 뜻이고, new 연산자는 선언된 클래스를 객체로 생성한다. 중괄호 { } 에는 인터페이스의 모든 추상 메소드를 구현하는 실체 메소드가 있어야 한다. 추가적으로 필드와 메소드를 선언할 수 있지만, 외부에서는 사용하지 못한다.

 

 

다음은 익명 구현 객체를 사용하는 예제이다.

// 인터페이스 RemoCon.java

public interface RemoCon {
    public void turnOn(); // 추상 메소드
    public void turnOff(); // 추상 메소드
}

 

// 익명 구현 클래스 Anonymous.java

public class Anonymous {
    RemoCon rmc = new RemoCon() {  // 익명 구현 객체
        @Override
        public void turnOn() {
            System.out.println("TV 켜기");
        }
        
        @Override
        public void turnOff() {
            System.out.println("TV 끄기");
        }
    };
    
    void method1() {
        RemoCon localVar = new RemoCon() {
            @Override
            public void turnOn() {
                System.out.println("Audio 켜기");
            }
        
            @Override
            public void turnOff() {
                System.out.println("Audio 끄기");
            }
        };
        
        localVar.turnOn();
    }
    
    void method2(RemoCon rc) {
        rc.turnOn();
    }
}

 

// Main.java

public class Main {
    public static void main(String[] args) {
        Anonymous am = new Anonymous();
        
        am.field.turnOn(); // 익명 객체 필드 사용
        am.method1(); // method1 내부의 익명 객체 변수 사용
        am.method2(
            new RemoCon() {
                @Override
                public void turnOn() {
                    System.out.println("SmartTV 켜기");
                }
                
                @Override
                public void turnOff() {
                    System.out.println("SmartTV 끄기");
                }
            }
        );
    }
}

마지막은 method2의 매개변수로 새로운 객체를 만들어서 넣어준 것이다. 이렇게 하면 method2의 rc의 값으로 새로운 객체가 대입되고 대입된 객체의 turnOn() 메소드가 실행된다.

 

 

익명 객체의 로컬 변수 사용

익명 객체 내부에서 바깥 클래스의 필드나 메소드는 제한없이 사용할 수 있다. 왜냐, 클래스가 생성되고 메모리에 남아있기 때문이다.

근데 메소드의 매개변수나 로컬 변수는 익명객체에서 사용할 수 없다. 왜냐, 매개 변수나 로컬 변수는 메소드 실행이 끝나면 스택 메모리에서 사라지기 때문에 익명 객체에서 사용할 수 없게 된다.

그렇다면 익명 객체 내부에서 메소드의 매개 변수나 로컬 변수를 사용할 수 있는 방법은 변수들이 final 특성을 가지면 된다.

자바 8 이후 부터는 익명 객체 내부에서 메소드의 매개변수나 로컬 변수가 사용되면 final 키워드 없이 선언해도 final 특성을 갖게 된다. 컴파일 시 final 키워드가 있다면 익명 객체 메소드 내부에 지역 변수로 복사되고, final 키워드가 없다면 익명 클래스의 필드로 복사된다. 근데 어디에 복사되는지는 신경 쓸 필요없고 final 특성을 갖는다는 것만 알면 된다.

 

익명 클래스와 로컬 클래스는 이름이 있냐 없냐의 차이일 뿐 동작 방식은 같다. 로컬 클래스는 메소드 안에서 선언한 클래스를 말한다.

로컬 클래스도 마찬가지이다. 로컬 클래스의 객체는 메소드 실행이 끝나도 힙 메모리에 존재한다. 근데 매개 변수나 로컬 변수는 스택 메모리에서 사라진다. 로컬 클래스에서도 똑같은 방법으로 final을 붙이면 사용이 가능하다.

'개발 관련 지식 > JAVA' 카테고리의 다른 글

Servlet  (0) 2020.06.14
Java 환경변수 설정이 필요한 이유  (2) 2020.06.14
정적 멤버와 static  (0) 2020.04.08
인스턴스 멤버와 this  (0) 2020.04.08
람다식 메소드 참조  (0) 2020.04.07