Java

익명 객체

공부처음하는사람 2024. 7. 6. 23:14

 

이름이 없는 객체를 익명객체라 한다. 명시적으로 클래스를 선언하지 않기 때문에 쉽게 객체를 생성할 수 있다.

익명객체는 필드값, 로컬 변수값, 매개변수값으로 주로 사용된다.

 

익명 객체는 클래스를 상속하고나 인터페이스를 구현해야만 생성할 수 있다.

상속시에 익명 상속 객체, 구현시에 익명 구현 객체라고 한다.

 

익명 자식객체

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

 

익명 자식 객체는 위와 같이 생성된다. 생성된 객체는 부모타입의 필드, 로컬변수, 매개변수 값으로 대입할 수 있다.

블록 안은 익명 자식 객체가 가져야 할 멤버이다. 부모타입의 멤버만 접근할 수 있기때문에 오버라이딩되는 코드가 작성된다.

 

package ch09.sec07.ex01;

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.ex03;

public class ButtonEx {
    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 btnCancel = new Button();

        class CancelListener implements Button.ClickListener {
            @Override
            public void onClick() {
                System.out.println("cancel button 클릭");
            }
        }

        btnCancel.setClickListener(new CancelListener());

        btnCancel.click();
    }
}

 

이전 중첩클래스의 Button 예제를 익명객체를 사용하여 리팩터링 한 코드이다. Ok와 Cancel을 비교해서 비교해보자

 

 


확인문제 오답노트

 

package ch09.sec07.ex;

public class Chatting {
    class Chat {
        void start() {
        }
        void sendMessage(String message) {
        }
    }

    void startChat(String chatId) {
        String nickName = null;
        nickName = chatId;

        Chat chat = new Chat() {
            @Override
            public void start() {
                while (true) {
                    String inputData = "안녕하세요";
                    String message = "[" + nickName + "] " + inputData;
                    sendMessage(message);
                }
            }
        };

        chat.start();
    }
}

 

위 코드에서 컴파일에러가 나는 이유는 익명 객체에서 로컬 변수 nickName이 사용되기 때문이다.

nickName은 사실상 final 이 됨

 

익명 클래스의 인스턴스가 생성될 때 로컬 변수는 스택프레임에 존재하게 되고, 익명객체의 인스턴스는 힙에 할당되는데

메소드가 종료되면 스택 프레임이 사라지므로 익명 클래스에서 참조되는 로컬 변수를 참조하려면 변수가 변경되지 않아야 함

 

위 코드에서는 nickName 변수는 null로 초기화되고, chatId로 할당된 후로 값이 변경되지 않았기 때문에 final로 간주되는 것

 

따라서 

 

package ch09.sec07.ex;

public class Chatting {
    class Chat {
        void start() {
        }
        void sendMessage(String message) {
        }
    }

    void startChat(String chatId) {
        final String nickName = chatId;  // 명시적으로 final 키워드 추가

        Chat chat = new Chat() {
            @Override
            public void start() {
                while (true) {
                    String inputData = "안녕하세요";
                    String message = "[" + nickName + "] " + inputData;
                    sendMessage(message);
                }
            }
        };

        chat.start();
    }
}

 

명시적으로 final 키워드를 추가했고, 불필요한 초기화를 제거함 (null로 초기화 했던 부분)

'Java' 카테고리의 다른 글

Object 클래스 / HashCode  (0) 2024.07.09
예외  (1) 2024.07.09
중첩 인터페이스  (0) 2024.07.06
로컬 클래스 / 바깥 클래스  (0) 2024.07.06
중첩 클래스 / 인스턴스 멤버 클래스 / 정적 멤버 클래스  (0) 2024.07.05