본문 바로가기
👨‍🏫Study/JAVA

[JAVA] 09 - 2 익명 객체

by 코푸는 개발자 2022. 3. 21.
728x90
익명 객체란 ?

 

- 말 그대로 이름이 없는 객체이다.

- 보통 우리는 클래스를 생성하고 여러 곳에서 그 클래스를 가지고 객체를 만든다. 

- new 연산자를 통해 클래스이름으로 생성자를 호출하는 방법을 사용한다.

- 여기서 우리는 여러 곳에서 사용하지 않고 오로지 특정 위치에서만 사용하고 싶을 때가 있다.

- 그 한 번의 사용을 위해 굳이 클래스를 만들어 사용하는 것은 귀찮은 일이다.

- 이럴 때 우리는 익명 객체를 사용하여 클래스를 명시적으로 선언하지 않고 바로 객체를 생성할 수 있다.  

- 인터페이스를 구현할 때도 익명 객체 생성 가능

 

 

일반적인(명시적으로 선언) 객체 생성 방법과 익명 자식 객체 생성 방법의 차이점

 

- 명시적인으로 객체 생성 방법

class Parent {} // 부모 클래스 생성

class Child extends Parent {} // 자식 클래스 생성

public class Example {
	
	// 필드의 초기값으로 자식 객체를 대입
	Parent field = new Child();
	
	// 로컬 변수로 자식 객체를 대입
	void method() {
		Parent localVar = new Child();
	}
}

 

- 익명 객체를 사용하는 방법

// 부모 클래스 생성
class Parent {
	void parentMethod() {}
} 

// 상속을 통해 객체 생성 - 익명 객체를 사용하는 방법
class Example2 {
	// 방법 1. 필드 선언
	Parent field = new Parent() { // 부모 생성자를 호출하는 코드
    // 부모 클래스를 상속해서 중괄호 안에 있는 내용처럼 자식 클래스를 선언한다는 의미
		int childField; // 필드나 메소드선언
		void childMethod() {}
		@Override // 부모 메소드를 재정의
		void parentMethod() {
			childField = 3; // 익명 자식 객체에서 정의된 필드와 메소드는
			childMethod(); // 익명 자식 객체 안에서만 사용 가능함
		} // 이유는 익명 자식 객체가 부모 타입 변수에 대입되어 부모 타입에 선언된것만 사용 가능하기 때문
	};
        
    void method_cannot_use_anonymous() {
		// field.childField = 3; // 익명 자식 객체 외부에서는 필드나 메소드 사용 불가
		// field.childMethod(); // parentMethod() 내부에서는 사용 가능
		field.parentMethod(); // 부모 메소드 호출은 가능
	}
		
	// 방법 2. 로컬 변수 선언 (방법1과 차이점은 메소드안에서 생성한다는 것만 다름)
	void method() {
		Parent localVar = new Parent() { 
			int childField;
			void childMethod() {}
			@Override
			void parentMethod() {} // 부모 메소드를 재정의
		};
	}
	
	// 방법 3. 매개 값으로 익명 객체 대입
	void method1(Parent parent) {} // 매개변수를 갖는 메소드 선언
	void method2() { // method1()을 호출
		method1( // method1()의 매개 값으로 익명 객체 대입
			new Parent() {
				int childField;
				void childMethod() {}
				@Override
				void parentMethod() {}
			}	
		);		
	}
}

 

 

익명 자식 객체 생성 예제

 

<부모 클래스 생성>

class Person {
	void wake() {
		System.out.println("6시에 일어납니다.");
	}
}

 

<익명 자식 객체 생성할 클래스 생성>

public class Anonymous {
		
		// 방법 1. 필드의 초기값으로 대입
		Person field = new Person() {
			void work() {
				System.out.println("출근합니다.");
			}
			
			@Override
			void wake() {
				System.out.println("6시에 일어납니다.");
				work();
			}
		};
		
		// 방법 2. 로컬 변수로 대입
		void method1() {	
			//로컬 변수 생성
			Person localVar = new Person() {
				void walk() {
					System.out.println("산책합니다.");
				}
				
				@Override
				void wake() {
					System.out.println("7시에 일어납니다.");
					walk();
				}
			};
			// 로컬 변수 사용
			localVar.wake();
		}
		
		// 방법 3. 매개 값으로 대입
		void method2(Person person) {
			person.wake();
		}
}

 

<익명 자식 객체를 이용하여 메소드 호출>

public class AnonymousExample {
	public static void main(String[] args) {
		// 객체 부터 생성
		Anonymous anony = new Anonymous();
		
		// 방법 1 사용. 필드 사용
		anony.field.wake(); // 6시에 일어납니다. 출근합니다.

		
		// 방법 2 사용. 로컬 변수 사용
		anony.method1(); // 7시에 일어납니다. 산책합니다.
		
		
		// 방법 3 사용. 매개변수로 대입하여 사용
		anony.method2(
			new Person() {
				void study() {
					System.out.println("공부합니다.");
				}
				@Override
				void wake() {
					System.out.println("8시에 일어납니다.");
					study();
				}				
			}
		); // 8시에 일어납니다. 공부합니다.
	}
}

 

인터페이스를 사용하여 익명 구현 객체 생성

 

- 일반적인 객체 생성 방법

interface RemoteControl {}

class TV implements RemoteControl {} // 구현 클래스 선언

public class Example_interface { // 일반적인 방법은 이렇게 A라는 클래스를 명시한 후 사용함
	RemoteControl field = new TV(); // 구현 객체 생성 후 인터페이스 타입의 필드에 대입
	
	void method() {
		RemoteControl localVar = new TV(); // 구현 객체 생성 후 인터페이스 타입의 로컬 변수에 대입
	}
}

 

- 익명 구현 객체 생성하는 방법

public class Example_interface2 {

	// 방법 1. 필드의 초기값으로 익명 구현 객체를 생성해서 대입하기	
	RemoteControl field = new RemoteControl() { 
		// new 연산자를 통해 중괄호 내부와 같이 선언된 구현 클래스를 객체로 생성한다.
		@Override
		public void turnOn() {} // 추상 메소드를 재정의한 실체 메소드
	};

	// 방법 2. 메소드 내에서 로컬 변수를 선언할 때 초기값으로 익명 구현 객체를 생성해서 대입
	void method() {
		RemoteControl localVar = new RemoteControl() {
			@Override
			public void turnOn() {}
			// 구현 객체 생성 후 인터페이스 타입의 로컬 변수에 대입
		};
	}
	
	// 방법 3. 메소드의 매개 값으로 익명 구현 객체를 생성해서 대입
	void method1(RemoteControl rc) {}
	void method2() {
		method1( // method1 호출
			new RemoteControl() { // method1() 매개값으로 익명 구현 객체를 대입
				@Override
				public void turnOn() {}
			}
		);
	}
	
}

 

익명 구현 객체 생성 예제

 

interface RemoteControl {
	public void turnOn();
	public void turnOff();
}

public class Anonymous2 {
	// 방법 1. 필드의 초기값으로 익명 구현 객체를 생성해서 대입하기	
	RemoteControl field = new RemoteControl() {
		@Override
		public void turnOn() {
			System.out.println("TV를 켭니다.");
		}
		
		@Override
		public void turnOff() {
			System.out.println("TV를 끕니다.");
		}
	};
	
	// 방법 2. 메소드 내에서 로컬 변수를 선언할 때 초기값으로 익명 구현 객체를 생성해서 대입
	void method1() {
		RemoteControl localVar = new RemoteControl() { // 로컬 변수 선언
			@Override
			public void turnOn() {
				System.out.println("Audio를 켭니다.");
			}
			
			@Override
			public void turnOff() {
				System.out.println("Audio를 끕니다.");
			}
		};
		localVar.turnOn(); // 로컬 변수 사용
		localVar.turnOff();
	}
	
	// 방법 3. 메소드의 매개 값으로 익명 구현 객체를 생성해서 대입
	void method2(RemoteControl rc) {
		rc.turnOn();
		rc.turnOff();
	}
}

 

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

		Anonymous2 anony = new Anonymous2(); // 객체 생성
		
		// 1. 익명 객체 필드 사용
		anony.field.turnOn(); 
		anony.field.turnOff();
		System.out.println();
		
		// 2. 익명 객체 로컬 변수 사용
		anony.method1();
		System.out.println();

		// 3. 익명 객체 매개값 사용
		anony.method2(new RemoteControl() { 
			@Override
			public void turnOn() {
				System.out.println("Smart TV를 켭니다.");
			}	
			@Override
			public void turnOff() {
				System.out.println("Smart TV를 끕니다.");
			}
		}
				);

	}
}

 

TV를 켭니다.
TV를 끕니다.

Audio를 켭니다.
Audio를 끕니다.

Smart TV를 켭니다.
Smart TV를 끕니다.

 

UI 프로그램에서 버튼의 클릭 이벤트 처리 예제 ( 익명 구현 객체를 이용 )

 

<Button 클래스>

public class Button {
	static interface OnClickListener {
		void onClick();
	} // 중첩 인터페이스 선언
	
	OnClickListener listener; // 인터페이스 타입 필드 선언
	
	// setter 메소드로 외부에서 구현 객체 받아 필드에 대입
	void setOnClickLister(OnClickListener listener) {
		this.listener = listener;
	}
	
	// touch를 하면 인터페이스를 통해 구현 객체의 메소드인 onClick()이 호출됨
	void touch() {
		listener.onClick();
	}
}

 

<Window 클래스>

public class Window {
	// 윈도우에는 두 개의 버튼이 있으므로 2개의 Button 객체 생성
	Button button1 = new Button();
	Button button2 = new Button();
	
	// button1의 클릭 이벤트 처리는 필드로 선언한 익명 구현 객체가 담당
	Button.OnClickListener listener = new Button.OnClickListener() {
		@Override 
		public void onClick() {
			System.out.println("전화를 겁니다.");
		}
	};
	
	// button2의 클릭 이벤트 처리는 setter 메소드의 매개값으로 준 익명 구현 객체가 담당 
	Window() {
		button1.setOnClickLister(listener); // 매개값으로 필드를 대입
		button2.setOnClickLister(new Button.OnClickListener() {
			@Override
			public void onClick() {
				System.out.println("메시지를 보냅니다.");
			}
		});
	}
}

 

<Main 클래스>

public class Main {
	public static void main(String[] args) {
		Window window = new Window();
		window.button1.touch(); // 전화를 겁니다.
		window.button2.touch(); // 메시지를 보냅니다. 
	}
}

 

 

익명 객체의 로컬 변수 사용

 

- 익명 객체 내부에서 매개변수나 로컬변수를 사용할 때 메소드 실행이 끝나도 지속적으로 사용이 가능하도록 매개변수나 로컬변수를 final로 선언하도록 요구한다.

-  이 경우 final을 생략해도 되는데 그러면 컴파일러가 자동으로 final 특성을 부여해준다.

 

<익명 객체의 로컬 변수>

interface Calculatable { // 인터페이스 선언
	int sum(); // 추상 메소드 선언
}

public class Anonymous {
	int field; // 필드 선언 (언제나 수정 가능) 
	
	public void method(final int arg1, int arg2) { 
		// 메소드 내부에서 선언된 매개변수나 로컬 변수들은 모두 수정이 불가능
		// 전부 final 특성을 지닌다.
		final int var1 = 40;
		int var2 = 50; 
		
		field = 10;
		
		// arg1 = 20; // final을 붙여도
		// arg2 = 20; // final을 안붙여도 둘다 값 수정이 불가능하다. 
		
		// var1 = 30; // final을 붙여도
		// var2 = 30; // final을 안붙여도 둘다 값 수정이 불가능하다.
		
		// 만약 값 수정을 시도한다면 아래 sum() 메소드 계산 식에서 컴파일 에러 발생함
		
		Calculatable calc = new Calculatable() {
			@Override
			public int sum() {
				int result = field + arg1 + arg2 + var1 + var2;
				return result;
			}
		};
		
		System.out.println(calc.sum());
	}
}

 

public class AnonymousExample {
	public static void main(String[] args) {
		Anonymous anony = new Anonymous();
		
		anony.method(20, 30); // 10 + 20 + 30 + 40 + 50 = 150
	}
}

 

728x90

댓글