익명 객체란?
- 클래스를 정의하지 않고 객체를 정의하는 방법 -> 1회용 클래스 사용 -> 재사용 불가능한 클래스 (객체를 1번 만드는 용도)
- 이벤트 객체, 스레드 객체, 람다식, 스트림 등에서 사용
1. 익명 자식 객체 생성
부모 타입으로 필드/변수를 선언하고, 자식 객체를 초기값으로 대입한다고 하자.
이 때 해야할 일은
- 부모클래스를 상속해서 자식 클래스 선언
- new 연산자를 이용해 자식 객체 생성
- 생성한 후 필드/로컬 변수에 대입
class Child extends Parent{ }
class A{
Parent field = new Child();
void method(){
Parent local = new Child();
}
}
하지만 자식 클래스가 재사용되지 않고, 단지 필드/변수의 초기값으로만 사용할 경우라면, 익명 자식 객체를 생성하여 초기값으로 대입하는 것이 좋은 방법이다.
public class AnonymousEx{
Parnet field = new Parent(){ // 필드 선언 시 초기값으로 익명 자식 객체를 생성해서 대입한 것
int childField;
// 부모 클래스의 메소드를 재정의
@Override
void parentMethod(){
}
// 메소드 내에서 로컬 변수 선언 시 초기값으로 익명 자식 객체를 생성해서 대입한다
void childMethod(){
Parent localVar = new Parent(){
int childField;
@Override
void parentMethod(){}
};
}
}; //익명 클래스의 선언은 끝에 무조건 세미클론이 붙는다!!
}
이 때, 익명 자식 객체에 새롭게 정의된 필드와 메소드는 익명 자식 객체 내부에서만 사용되고, 외부에서는 필드와 메소드에 접근할 수 없다.
why??
부모 타입의 변수에 대입되기 때문에, 부모 타입에 선언된 것만 사용할 수 있기 때문이다.
public class AnonymousEx2{
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(); // 사용 가능
}
}
2. 익명 구현 객체 생성
이번에는 인터페이스 타입으로 필드와 변수를 선언하고, 구현 객체를 초기값으로 대입하는 경우를 생각해보자.
이 때 해야할 일은
- 구현 클래스 선언
- new 연산자를 이용해서 구현 객체 생성
- 생성한 객체를 필드나 로컬 변수에 대입
익명 자식 객체 생생 때와 마찬가지로, 구현 클래스가 재사용 되지 않고 해당 필드/변수의 초기값으로만 사용되는 경우라면 익명 구현 객체를 초기값으로 대입하는 것이 좋다. 주의할 점은, 중괄호 {} 안에는 인터페이스에 선언된 모든 추상 메소드의 실체 메소드를 작성해야 한다. 그렇지 않으면 컴파일 에러가 발생한다.
public interface RemoteControl{
public void trunOn();
public void turnOff();
}
public class AnonymousEx3{
RemoteControl field = new RemoteControl(){
@Override
public void trunOn(){
System.out.println("TV 켜기");
}
@Override
public void turnOff(){
System.out.println("TV 끄기");
}
};
void method(){
RemoteControl locVal = new RemoteControl(){
@Override
public void turnOn(){
System.out.println("오디오 켜기");
}
@Override
public void turnOff(){
System.out.println("오디오 끄기");
}
};
local.trunOn();
}
void method2(RemoteControl rc){
rc.turnOn();
}
}
public class AnonymousTest{
public static void main(String[] args){
AnonymousEx3 anony = new AnonymousEx3();
// 익명 객체 필드 사용하기
anony.field.trunOn(); // TV 켜기
// 익명 객체 로컬변수 사용하기
anony.method(); // 오디오 켜기
// 익명 객체 매개값 사용하기
anony.method2(new RemoteControl(){
@Override
public void turnOn(){
System.out.println("smart TV 켜기");
}
@Override
public void turnOff(){
System.out.println("smart TV 끄기");
}
}); // 스마트 TV 켜기
}
}
익명 객체의 로컬 변수 사용
익명 객체 내부에서는 외부 클래스의 필드와 메소드를 사용할 수 있다. 하지만 메소드의 매개변수와 로컬변수를 익명 객체에서 사용할 때는 문제가 있다.
메소드 내에서 생성된 익명 객체는 메소드의 실행이 끝나도 힙 메모리에 존재하기 때문에 계속해서 사용할 수 있다. 하지만 매개변수나 로컬변수는 메소드 실행이 끝나면 스택 메모리에서 사라지기 때문에 익명 객체에서 사용할 수 없게 되므로 문제가 발생한다.
익명 객체 내부에서 메소드의 매개변수나 로컬변수를 사용할 경우, 이 변수들은 final 특성을 가져야 한다.
* java8 이후부터는 final 키워드를 따로 붙이지 않아도 변수의 값이 변경되지 않는다면 final 특성을 가진다!!
참고 -> https://codechacha.com/ko/java-effectively-final-vs-final/
public interface Calculatable{
public int sum();
}
public class Anonymous{
public int field;
public void method(final int arg1, int arg2){
final int var1 = 0;
int var2 = 0;
field = 10;
Calculatable calc = new Calculatable(){
@Override
public int sum(){
int result = field + arg1 + arg2 + var1 + var2;
// 익명 객체 내부에서 메소드의 매개변수와 로컬변수 사용 -> 얘네는 final 특성 가지고 있음
return result;
}
};
System.out.println(clac.sum());
}
}
public class AnonymousExample{
public static void main(String[] args){
Anonymous anony = new Anonymous();
anony.method(0, 0); // 출력 결과는 10 + 0 + 0 + 0 + 0 = 10
}
}
'Language > Java' 카테고리의 다른 글
[Java] Optional<T> 클래스 (0) | 2022.07.05 |
---|---|
[Java] 메소드 레퍼런스(Method reference, ::의 의미) (0) | 2022.07.05 |
[Java] Stream API - filter, map, flatMap (0) | 2022.07.04 |
[Java] 람다식(lambda expression) (0) | 2022.07.04 |
[Java] Input/Output 정리 (0) | 2021.07.13 |