본문 바로가기

Language/Java

[Java] 익명 객체(anonymous object)

728x90
반응형

익명 객체란?

  • 클래스를 정의하지 않고 객체를 정의하는 방법 -> 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
    }
}
728x90
반응형