코딩 이야기
JAVA 익명 객체 본문
익명 객체는 이름이 없는 객체를 말합니다. 익명 객체를 만들려면 조건이 있습니다. 어떤 클래스를 상속하거나 인터페이스를 구현해야만 합니다. 일반적인 경우에는 다음과 같이 명시적으로 클래스 이름을 주고 선언합니다.
[상속]
class 클래스이름1 extends 부모클래스 {...}
부모클래스 변수 = new 클래스 이름1();
[구현]
class 클래스이름2 implenents dlsxjvpdltm {...}
인터페이스 변수 = new 클래스이름2();
그러나 익명 객체를 생성할 때는 다음과 같이 클래스 이름이 없습니다.
[상속]
부모클래스 변수 = new 부모 클래스() {...};
[구현]
인터페이스 변수 = new 인터페이스() {...};
이 경우 부모 클래스 변수는 이름이 없는 자식 객체를 참조하고, 인터페이스 변수는 이름이 없는 구현 객체를 참조하게 됩니다.
익명 자식 객체 생성
부모 타입의 필드 또는 변수를 선언하고 자식 객체를 초기값으로 대입하는 경우를 생각해보겠습니다. 우선 부모 클래스를 상속해서 자식 클래스를 선언합니다. 그리고 new 연산자를 이용해서 자식 객체를 생성한 후 부모 타입의 필드 또는 변수에 대입하는 것이 일반적입니다.
class Child extends Parent { } // 자식 클래스 선언
class A {
praent fudeld = new Child(); //필드에 자식 객체를 대입
void method() {
Parent localVar = new Child(); //로컬 변수에 자식 객체를 대입
}
}
자식 클래스를 명시적으로 선언하는 이유는 어디서건 이미 선언된 자식 클래스로 간단히 객체를 생성해서 사용할 수 있기 때문입니다. 우리는 이것을 재사용성이 높다고 말합니다.
그러나 자식 클래스가 재사용되지 않고, 오로지 특정 위치에서 사용할 경우라면 자식 클래스를 명시적으로 선언하는 것은 귀찮은 작업이 됩니다. 이 경우에는 익명 자식 객체를 생성해서 사용하는 것이 좋은 방법입니다. 익명 자식 객체를 생성하는 방법은 다음과 같습니다.
부모클래스 [필드|변수] = new 부모클래스 (매개값, ... ) {
//필드
//메소드
};
'부모 클래스(매개값, ...) {...}'은 부모 클래스를 상속해서 중괄호 {}와 같이 자식 클래스를 선언하라는 뜻입니다. 그리고 new 연산자는 이렇게 선언된 자식 클래스를 객체로 생성합니다.
'부모 클래스(매개값, ...) '은 부모 생성자를 호출하는 코드로, 매개값은 부모 생성자의 매개 변수에 맞게 입력하면 됩니다. 중괄호{} 내부에는 필드나 메소드를 선언하거나 부모 클래스의 메소드를 재정의(오버라이딩)하는 내용을 작성합니다.
일반 클래스와의 차이점은 생성자를 선언할 수 없는다는 것입니다.
다음은 필드를 선언할 때 초기값으로 익명 자식 객체를 생성해서 대입하는 예입니다.
class A {
Parent field = new Parent() { //A 클래스의 필드 선언
int childField;
void ChildMethod() { }
@Override
void parentMethod() { } //parent의 메소드를 재정의
};
}
다음은 메소드 내에서 로컬 변수를 선언할 때 초기값으로 익명 자식 객체를 생성해서 대입하는 예입니다.
class A {
void method() {
parent localVar = new Parent() { // 로컬 변수 선언
int childField;
void childMethod() { }
@Override
void parentMethod() [ } // parent 의메소드를 재정의
};
}
}
다음은 메소드의 매개변수가 부모타입일 경우 메소드를 호출하는 코드에서 익명 자식 객체를 생성해서 매개값으로 대입하는 방법입니다.
class A {
void method1 (Parent parent) { }
void method2() {
method1( //method1() 메소드 호출
new Parent() { //method1() 매개값으로 익명 자식 객체를 대입
int childField;
void chilMethod() { }
@Override
void parentMethod() { }
}
);
}
}
익명 자식 객체에 새롭게 정의된 필드와 메소드는 익명 자식 객체 내부에서 사용되고, 외부에서는 접근할 수 없습니다. 왜냐하면 익명 자식 객체는 부모 타입 변수에 대입되므로 부모 타입에 선언된 것만 사용할 수 있기 때문입니다.
예를 들어 다음 코드에서 childField 필드와 childMethod() 메소드는 parentMethod()메소드내에서 사용이 가능하나, A클래스의 필드인 field로는 접근할 수 없습니다.
class A [
Parent field = new Parnet() {
int childField;
void childMethod() { }
@Override
void parentMethod() {
childField = 3;
childMethod();
}
};
void method() {
field.childField = 3; //(x) 접근불가
field.childMethod(); //(x) 접근불가
field.parentMethod(); //(o) 접근가능
}
}
부모클래스
package ch09;
public class Person {
void wake() {
System.out.println("7시에 일어납니다.");
}
}
익명 자식 객체 생성
package ch09;
public class Anonymous {
//필드 초기값으로 대입
Person field = new Person() {
void work() {
System.out.println("출근합니다.");
}
@Override
void wake() {
System.out.println("6시에 일어납니다.");
work();
}
};
void method1() {
//로컬 변수값으로 대입
Person localVar = new Person() {
void walk() {
System.out.println("산책합니다.");
}
@Override
void wake() {
System.out.println("7시에 일어납니다.");
walk();
}
};
//로컬 변수 사용
localVar.wake();
}
void method2(Person person) {
person.wake();
}
}
익명 자식 객체 생성
package ch09;
public class AnonymousExample {
public static void main(String[] args) {
// TODO Auto-generated method stub
Anonymous anony = new Anonymous();
// 익명 객체 필드 사용
anony.field.wake();
// 익명 객체 로컬 변수 사용
anony.method1();
// 익명 객체 매개값 사용
anony.method2(
new Person() {
void study() {
System.out.println("공부합니다.");
}
@Override
void wake() {
System.out.println("8시에 일어납니다.");
study();
}
}
);
}
}
익명 구현 객체 생성
인터페이스 타입의 필드 또는 변수를 선언하고, 구현객체를 초기값으로 대입하는 경우를 생각해보겠습니다. 우선 구현 클래스를 선언합니다. 그리고 new 연산자를 이용해서 구현 객체를 생성한 후 인터페이스 타입의 필드 또는 로컬 변수에 대입하는 것이 일반적입니다.
class TV implements RemoteControl { }
class A {
RemoteControl field = new TV (); //필드에 구현 객체를 대입
void method() {
RemoteControl localVar = new TV(); //로컬 변수에 구현객체를 대입
}
}
구현 클래스를 명시적으로 선언하는 이유는 어디서건 이미 선언된 구현 클래스로 간단히 객체를 생성해서 사용할 수 있기 때문입니다. 우리는 이것을 재사용성이 높다고 말합니다.
그러나 구현 클래스가 재사용되지 않고, 오로지 특정 위치에서 사용할 경우라면 구현 클래스를 명시적으로 선언하는 것은 귀찮은 작업이 됩니다. 이 경우에는 익명 구현 객체를 생성해서 사용하는 것이 좋습니다. 익명 구현 객체를 생성하는 방법은 다음과 같습니다.
인터페이스 [필드|변수] = new 인터페이스() {
// 인터페이스에 선언된 추상 메소드의 실체 메소드 선언
// 필드
// 메소드
};
'인터페이스() { ... } '는 인터페이스를 구현해서 중괄호 {}와 같이 클래스를 선언하라는 뜻입니다. 그리고 new 연산자는 이렇게 선언된 구현 클래스를 객체로 생성합니다. 중괄호 {} 에는 인터페이스에 선언된 모든 추상 메소드의 실체 메소드를 작성(재정의)해야 합니다. 그렇지 않으면 컴파일 에러가 발생합니다.
**추가로 필드와 메소드를 선언할 수 있지만 실체 메소드에서만 사용이 가능하고 외부는 불가능합니다.
다음은 필드를 선언할 때 초기값으로 익명 구현 객체를 생성해서 대입하는 예입니다.
class A {
RemoteControl field = new RemoteControl; // 클래스 A의 필드선언
@Override
void turnOn() { } //RemoteControl 인터페이스의 추상 메소드에 대한 실체 메소드
};
}
다음은 메소드 내에서 로컬 변수를 선언할 때 초기값으로 익명 구현 객체를 생성해서 대입하는 예입니다.
void method() {
RemoteControl localVar = new RemoteControl() { //로컬 변수 선언
@Override
void turnOn() { } //RemoteControl 인터페이스의 추상 메소드에 대한 실체 메소드
};
}
다음은 메소드의 매개변수가 인터페이스 타입일 경우 메소드를 호출하는 코드에서 익명 구현 객체를 생성해서 매개값으로 대입하는 예입니다.
class A {
void method1(RemoteControl rc) { }
void method2() {
method1( //method1()메소드 호출
new RemoteControl() { // method1()의 매개값으로 익명 구현 객체를 대입
@Override
void turnOn() { }
}
);
}
}
인터페이스
package ch09;
public interface RemoteControl {
public void turnOn();
public void turnOff();
}
익명 구현 객체 생성
package ch09;
public class Anonymous1 {
// 필드 초기값으로 대입
RemoteControl fiedl = new RemoteControl() {
@Override
public void turnOn() {
System.out.println("TV를 켭니다.");
}
@Override
public void turnOff() {
System.out.println("TV를 끕니다.");
}
};
void method1() {
// 로컬 변수값으로 대입
RemoteControl localVar = new RemoteControl() {
@Override
public void turnOn() {
System.out.println("Audio를 켭니다");
}
@Override
public void turnOff() {
System.out.println("Audio를 끕니다.");
}
};
// 로컬 변수 사용
localVar.turnOn();
}
void method2(RemoteControl rc) {
rc.turnOn();
}
}
익명 구현 객체 생성
package ch09;
public class AnonymousExample1 {
public static void main(String[] args) {
// TODO Auto-generated method stub
Anonymous1 anony = new Anonymous1();
// 익명 객체 필드 사용
anony.fiedl.turnOn();
// 익명 객체 로컬 변수 사용
anony.method1();
// 익명 객체 매개값 사용
anony.method2(new RemoteControl() {
@Override
public void turnOn() {
System.out.println("SmartTV를 킵니다.");
}
@Override
public void turnOff() {
System.out.println("SmartTV를 끕니다.");
}
}
);
}
}
다음은 윈도우 및 안드로이드 등의 UI프로그램에서 버튼의 클릭 이벤트를 처리하기 위해 익명 구현 객체를 이용하는 방법을 보여줍니다.
UI클래스
package ch09;
public class Button1 {
OnClickListener listener; //인터페이스 타입 필드
void setOnClickListener(OnClickListener listener) { // 매개 변수의 다형성
this.listener = listener;
}
void touch() { // 구현 객체의 onClick(0 메소드 호출
listener.onClick();
}
static interface OnClickListener { // 중첩 인터페이스
void onClick();
}
}
Button 클래스의 내용을 보면 중첩 인터페이스(OnclickListener) 타입으로 필드(listener) 를 선언하고 Setter 메소드(setOnClickListener())로 외부에서 구현 객체를 받아 피륻에 대입합니다. 버튼 이벤트가 발생했을 때 touch() 메소드가 실행되었을 때) 인터페이스를 통해 구현 객체의 메소드를 호출(listener.onClick())합니다.
다음 window 클래스를 2개의 Button 객체를 가지고 있는 창이라고 가정해 보겠습니다. 첫 번째 button1의 클릭 이벤트 처리는 필드로 선언한 익명 구현객체가 담당하고, 두번째 button2의 클릭 이벤트처리는 setOnClickListener()를 호출할 때 매개값으로 준 익명 구현 객체가 담당하도록 했습니다.
UI 클래스
package ch09;
public class Window {
Button1 button1 = new Button1();
Button1 button2 = new Button1();
//필드 초기값으로 대입
Button1.OnClickListener listener = new Button1.OnClickListener() {
@Override
public void onClick() {
System.out.println("전화를 겁니다.");
}
};
Window() {
button1.setOnClickListener(listener); // 매개값으로 필드 대입
button2.setOnClickListener(new Button1.OnClickListener() {
@Override
public void onClick() {
System.out.println("메세지를 보냅니다.");
}
});
}
}
실행 클래스
package ch09;
public class Main1 {
public static void main(String[] args) {
// TODO Auto-generated method stub
Window w = new Window();
w.button1.touch();
w.button2.touch();
}
}
익명 객체의 로컬 변수 사용
다음 예제는 매개 변수와 로컬 변수가 익명 객체 내부에서 사용할 때 매개 변수와 로컬 변수가 final 특성을 갖고 있음을 보여줍니다.
인터페이스
package ch09;
public interface Calculatable {
public int sum();
}
익명 객체의 로컬 변수 사용
package ch09;
public class Anonymous2 {
private int field;
public void method(final int arg1, int arg2) {
final int var1 = 0;
int var2 = 0;
field = 10;
//arg1 = 20; (x)
//arg2 = 20; (x)
//var1 = 30; (x)
//var2 = 30; (x)
Calculatable calc = new Calculatable() {
@Override
public int sum() {
int result = field + arg1 + arg2 + var1 +var2;
return result;
}
};
System.out.println(calc.sum());
}
}
익명 객체의 로컬 변수 사용
package ch09;
public class AnonymousExample2 {
public static void main(String[] args) {
// TODO Auto-generated method stub
Anonymous2 anony = new Anonymous2();
anony.method(0, 0);
}
}
'JAVA' 카테고리의 다른 글
JAVA 예외 처리 (0) | 2023.02.22 |
---|---|
JAVA 중첩 클래스와 중첩 인터페이스 (0) | 2023.02.17 |
JAVA 타입변환과 다형성(인터페이스) (0) | 2023.02.16 |
JAVA 인터페이스 (0) | 2023.02.15 |
JAVA 추상 클래스 (0) | 2023.02.15 |