본문 바로가기
언어/JAVA

[중첩 인터페이스] 익명 객체(익명 자식 객체, 익명 구현 객체)

by 코딩맛집 2023. 2. 5.

9.6 중첩 인터페이스

중첩 인터페이스는 클래스의 멤버로 선언된 인터페이스를 말한다.

인터페이스를 클래스 내부에 선언하는 이유는 해당 클래스와 긴밀한 관계를 맺는 구현 객체를 만들기 위해서이다.

 

인터페이스도 접근 제한자와 static과 관련하여 중첩 클래스와 동일하다.

 

중첩 인터페이스는 안드로이드와 같은 UI 프로그램에서 이벤트를 처리할 목적으로 많이 활용된다. 예를 들어 버튼을 클릭했을 때 이벤트를 처리할 객체는 중첩 인터페이스를 구현해서 만든다.

package ch09.sec06.exam03;

public class Button {
    // 정적 중첩 인터페이스
    public static interface ClickListener {
        // 추상 메소드
        void onClick();
    }

    // 필드
    private ClickListener clickListener;

    // 메소드
    public void setClickListener(ClickListener clickListener) {
        this.clickListener = clickListener;
    }

    public void click(){
        this.clickListener.onClick();
    }
}

Button 클래스에 ClickListener 타입의 필드와 Setter를 추가해서 외부에서 Setter를 통해 ClickListener 구현 객체를 필드에 저장할 수 있도록 한다. Button이 클릭되었을 때 실행할 메소드로 click()을 추가한다. ClickListener 인터페이스 필드를 이용해서 onClick() 추상 메소드를 호출한다. 이제 버튼을 이용하는 실행 클래스를 작성해보자.

package ch09.sec06.exam03;

public class ButtonExample {
    public static void main(String[] arg){
        // ok 버튼 객체 생성
        Button btnOk = new Button();

        // ok 버튼 클릭 이벤트를 처리할 ClickListener 구현 클래스 (로컬 클래스)
        class OkListener implements  Button.ClickListener {
            @Override
            public void onClick(){
                System.out.println("Ok 버튼을 클릭했습니다.");
            }
        }

        // ok 버튼 객체에 ClickListener 구현 객체 주입
        btnOk.setClickListener(new OkListener());

        // ok 버튼 클릭하기
        btnOk.click();
        
        // cancel 버튼 객체 생성
        Button btnCancel = new Button();
        
        // cancel 버튼 클릭 이벤트를 처리할 clicklistener 구현 클래스(로컬 클래스)
        class CancelListener implements Button.ClickListener {
            @Override
            public void onClick(){
                System.out.println("Cancel 버튼을 클릭했습니다.");
            }
        }
        // cancel 버튼 객체에 ClickListener 구현 객체 주입
        btnCancel.setClickListener(new CancelListener());
        
        // cancel 버튼 클릭하기
        btnCancel.click();
    }
}

implements 라인은 버튼 이벤트를 처리할 ClickListener 구현 클래스로, onClick() 메소드를 재정의해서 버튼이 클릭되었을 떄 해야할 일을 코딩한다. setClickListener(new 구현 객체) 라인은 버튼이 앞으로 클릭되었을 때 처리를 담당할 ClickListener 구현 객체를 설정하는 코드이다.

UI 프로그램에서는 마우스로 버튼을 클릭하지만, 이 예제는 btnCancel.click()라인에서 click() 메소드를 호출한다. 이 메소드가 호출되면 버튼에 설정된 ClickListener 구현 객체의 onClick() 메소드가 실행된다. 버튼에 어떤 ClickListener 구현 객체가 설정되었느냐에 따라 실행 결과는 달라진다.(다형성)

 

9.7 익명 객체

익명 객체는 이름이 없는 객체를 말한다. 명시적으로 클래스를 선언하지 않기 때문에 쉽게 객체를 생성할 수 있다는 장점이 있다. 익명 객체는 필드값, 로컬 변수값, 매개변수값으로 주로 사용된다.

익명 객체는 클래스를 상속하거나 인터페이스를 구현해야만 생성할 수 있다. 클래스를 상속해서 만들 경우 익명 자식 객체라고 하고, 인터페이스를 구현해서 만들 경우 익명 구현 객체라고 한다.

 

익명 자식 객체

익명 자식 객체는 부모 클래스를 상속받아 다음과 같이 생성된다. 이렇게 생성된 객체는 부모 타입의 필드, 로컬 변수, 매개변수의 값으로 대입할 수 있다.

new 부모생성자(매개값, ...){
    //필드
    //메소드
}

중괄호 블록 안의 필드와 메소드는 익명 자식 객체가 가져야 할 멤버로, 중괄호 블록 안에서만 사용할 수 있다. 익명 자식 객체는 부모 타입에 대입되므로 부모 타입에 선언된 멤버만 접근할 수 있기 때문이다. 중괄호 블록 안에는 주로 부모 메소드를 재정의하는 코드가 온다.

다음 예제는 Tire 클래스의 익명 자식 객체를 생성해서 필드, 로컬 변수, 매개변수의 값으로 사용하는 방법을 보여준다. Tire 클래스는 roll() 메소드를 가지고 있지만, 익명 자식 객체는 roll()을 재정의해 실행 내용을 변경한다.(다형성)

package ch09.sec07.exam01;

public class Tire {
    public void roll(){
        System.out.println("일반 타이어가 굴러갑니다.");
    }
}
package ch09.sec07.exam01;

public class Car {

    // 필드에 Tire 객체 대입
    private Tire tire1 = new Tire();

    // 필드에 익명 자식 객체 대입
    private Tire tire2 = new Tire() {
        @Override
        public void roll(){
            System.out.println("익명 자식 Tire 객체 1이 굴러갑니다.");
        };
    };

    // 메소드(필드 이용)
    public void run1(){
        tire1.roll();
        tire2.roll();
    }

    // 메소드(로컬 변수 이용)
    public void run2(){
        Tire tire = new Tire(){
            @Override
            public void roll(){
                System.out.println("익명 자식 Tire 객체 2가 굴러갑니다.");
            }
        };
        tire.roll();
    }

    // 메소드 (매개변수 이용)
    public void run3(Tire tire){
        tire.roll();
    }
}
package ch09.sec07.exam01;

public class CarExample {
    public static void main(String[] args){

        // Car 객체 생성
        Car car = new Car();

        // 익명 자식 객체가 대입된 필드 사용
        car.run1();

        // 익명 자식 객체가 대입된 로컬변수 사용
        car.run2();

        // 익명 자식 객체가 대입된 매개변수 사용
        car.run3(new Tire() {
            @Override
            public void roll(){
                System.out.println("익명 자식 Tire 객체 3이 굴러갑니다.");
            }
        });
    }
}
//출력
//일반 타이어가 굴러갑니다.
//익명 자식 Tire 객체 1이 굴러갑니다.
//익명 자식 Tire 객체 2이 굴러갑니다.
//익명 자식 Tire 객체 3이 굴러갑니다.

익명 자식 객체가 부모 타입에 대입되면 부모 메소드 roll()을 호출할 경우, 재정의된 익명 자식 객체의 roll() 메소드가 실행되는 것을 볼 수 있다(다형성).

 

익명 구현 객체

익명 구현 객체는 인터페이스를 구현해서 다음과 같이 생성된다. 이렇게 생성된 객체는 인터페이스 타입의 필드, 로컬변수, 매개변수의 값으로 대입할 수 있다. 익명 구현 객체는 안드로이드와 같은 UI 프로그램에서 이벤트를 처리하는 객체로 많이 사용된다.

new 인터페이스(){
    //필드
    //메소드
}

중괄호 블록 안의 필드와 메소드는 익명 구현 객체가 가져야 할 멤버로, 중괄호 블록 안에서만 사용할 수 있다. 그 이유는 익명 구현 객체는 인터페이스 타입에 대입되므로 인터페이스 타입에 선언된 멤버만 접근할 수 있기 때문이다. 중괄호 블록 안에는 주로 인터페이스의 추상 메소드를 재정의하는 코드가 온다.

다음 예제는 RemoteControl 인터페이스의 익명 구현 객체를 생성해서 필드, 로컬 변수, 매개변수 값으로 사용하는 방법을 보여준다. 익명 구현 객체는 roll() 메소드를 재정의해서 실행 내용을 가지고 있다(다형성.)

package ch09.sec07.exam02;

public interface RemoteControl {
    
    //추상 메소드
    void turnOn();
    void turnOff();
}
package ch09.sec07.exam02;

public class Home {
    // 필드에 익명 구현 객체 대입
    private RemoteControl rc = new RemoteControl() {
        @Override
        public void turnOn() {
            System.out.println("TV를 켭니다.");
        }

        @Override
        public void turnOff() {
            System.out.println("TV를 끕니다.");
        }
    };

    // 메소드(필드 이용)
    public void use1(){
        rc.turnOn();
        rc.turnOff();
    }

    //메소드(로컬 변수 이용)
    public void use2(){
        //로컬 변수에 익명 구현 객체 대입
        RemoteControl rc = new RemoteControl() {
            @Override
            public void turnOn() {
                System.out.println("에어컨을 켭니다.");
            }

            @Override
            public void turnOff() {
                System.out.println("에어컨을 끕니다.");
            }
        };
        rc.turnOn();
        rc.turnOff();
    }

    // 메소드(매개변수 이용)
    public void use3(RemoteControl rc){
        rc.turnOn();
        rc.turnOff();
    }
}
package ch09.sec07.exam02;

public class HomeExample {
    public static void main(String[] args){
        // Home 객체 생성
        Home home = new Home();
        
        // 익명 구현 객체가 대입된 필드 사용
        home.use1();
        
        // 익명 구현 객체가 대입된 로컬 변수 사용
        home.use2();
        
        // 익명 구현 객체가 대입된 매개변수 사용
        home.use3(new RemoteControl() {
            @Override
            public void turnOn() {
                System.out.println("난방을 켭니다.");
            }

            @Override
            public void turnOff() {
                System.out.println("난방을 끕니다.");
            }
        });
    }
}

다음 예제는 9.6 중첩 인터페이스의 예제를 수정한 것으로, 버튼 이벤트 처리 객체를 익명 구현 객체로 대체한 것이다. Setter를 호출할 때 매개값으로 ClickListener 익명 구현 객체를 대입했다. 명시적인 구현 클래스를 생성하지 않기 때문에 코드가 간결해진 것을 볼 수 있다.

package ch09.sec07.exam03;

public class ButtomExample {
    public static void main(String[] args){
        Button btnOk = new Button();

        btnOk.setClickListener(new Button.ClickListener(){
            @Override
            public void onClick(){
                System.out.println("Ok 버튼을 클릭했습니다.");
            }
        });

        btnOk.click();

        Button vtnCancel = new Button();

        btnCancel.setClickListener(new Button.ClickListener(){
            @Override
            public void onClick(){
                System.out.println("Cancel 버튼을 클릭했습니다.");
            }
        });

        btnCancel.click();
    }
}

'언어 > JAVA' 카테고리의 다른 글

[멀티 스레드2]  (0) 2023.02.27
[chapter 9] 확인 문제  (0) 2023.02.06
[중첩 클래스]  (0) 2023.02.05
[chapter5] 확인 문제  (2) 2023.01.30
[인터페이스]  (0) 2023.01.30