프로그래밍/Java

[Java] 익명객체(익명클래스)란? (이 글 하나로 한방에 정리!)

Lim-Ky 2021. 12. 10. 00:46
반응형

익명객체(익명클래스) 란?

이번시간에는 자바 익명객체(익명클래스)에 대해서 알아보도록 하겠습니다.

익명객체(익명클래스) 말그대로.. 이름이 없는 객체? 클래스?,,,그래서 무명클래스라고도 합니다.

언어의 사전적인 의미는 파악이 되셨을거라 생각됩니다.

그럼 도대체 이름도 없고 뭔가 근본도 없는것 같은 이 익명객체(익명클래스)를 왜 사용해야하는지.. 바로 설명들어가겠습니다.

반응형

 

익명객체(익명클래스) 를 사용하는 이유!!

이름 부터 눈치 채셨겠지만, 이름이 없다는 것은 별로 기억되지 않아도 된다는 거겠죠..

나중에 다시 불러질 이유가 없다는 뜻입니다. 이 말을 좀 더 있어보이게 말하면...

프로그램에서 일시적으로 한번만 사용되고 버려지는 객체입니다. 좀 더 풀어서 생각해보면 일시적으로 사용된다는 것은 나중에 재사용이 되지 않는다는 것이며, 재사용이 될 필요가 없다는 뜻은 확장성이 그렇게 좋지 못하다는 뜻입니다.

자바에서 확장성을 고려해서 코딩을하고 설계를 하는 것도 중요합니다. 정말 중요하죠.. 하지만, 최소한의 확장성이 성립되려면 어느정도 유지보수에 대한 이점이 있어야 하는데, 오히려 확장성을 고려해서 설계를 했다간 유지보수에서 더 불리한 경우가 있습니다(아래 예시 참고). 자,,,익명클래스를 사용해야하는 이유를 정리하자면....


1. 프로그램 내에서 일시적으로(단발성으로) 한번만 사용되어야 하는 객체일 경우
   -> UI 이벤트처리, 스레드 객체 등 (단발성 이벤트 처리)

2. 재사용성이 없고, 확장성을 활용하는 것이 유지보수에서 더 불리할 때

  -> 비즈니스 로직이 정말 재각각이며, 재사용성이 전혀없어 매번 클래스를 생성해야하는 비용이 더 많을때

 

극단적인 예시를 들어보겠습니다.

웃긴 예시긴 한데 ㅋㅋ

곤충이라는 부모클래스를 상속받는 거미라는 구현클래스가 있다.

근데...세상을 보아하니 거미는 "전 세계에 약 3만 종이 알려져 있으며 한국에는 약 600종이 분포한다."

암튼, 이 거미의 공통적인 특징도 없고, 잠깐 프로그램 로직에서 단발성(단 한번)으로 등장해서 거미줄을 각자의 방식으로 치고 영원히 사라진다고 칩시다.

그러면 여러분은 3만종에 거미 클래스를 만들어 유지보수하는게 나을지 아니면 코드에서 일시적으로 정의하여 쓰고 버릴지...생각해보시면 됩니다.

 

한번만 사용될 목적인데 3만개의 클래스를 별도로 만들어줘야하고;;;, 코드에서도 객체를 생성해야한다.???

-> 생각만해도 귀찮고 무의미, 유지보수에 불리함

 

클래스를 별도로 만들 필요없이 코드에서 익명객체를 생성/정의하여 단 한번의 쓰임을 다하고 소멸한다.

-> 덜 귀찮고 합리적임, 유지보수에 유리함

 

답이 나왔을거라고 생각됩니다. 여러분들은 코딩을 하기전 어떤 객체가 이런 귀찮고 재사용하지 않을 것 같은 느낌이 올 때 익명클래스, 익명객체를 사용하시면 됩니다.

 

반응형


익명객체(익명클래스) 구현하는 방법 (익명 자식객체 생성, 익명 구현객체 생성)

 

익명객체(클래스)를 구현하는 방법은 크게 2가지 입니다.

부모/자식간 상속아래 익명 자식객체를 생성할 것인가?

아니면, 인터페이스를 구현한 익명 구현객체를 생성할 것인가? (다음 시간에 다루겠음)

이번시간에는 명 자식객체에 대해서 알아보겠습니다.

 

익명 자식객체 생성 방법에는 아래 3가지방법으로 구현할 수 있습니다.
1. 필드의 초기값
2. 로컬변수의 초기값
3. 매개변수의 매개값

 

정의는 아래와 같이 하면 됩니다. (세미콜론 ; 으로 마무리)

 

부모클래스 [필드|변수] = new 부모클래스(매개값, ... ) {

 

};

 

자 그럼 코드를 보면서 설명하도록 하겠습니다.

package Anonymous;

public class Insect {

	void attack(){
		System.out.println("곤충은 공격을 한다");
	}
}

우선 곤충 Insect 이라는 부모클래스를 정의했습니다.

향후 Insect 부모 클래스는 일시적으로 정의된 익명객체를 담아둘 변수로 사용되기도 하고, 익명객체가 무엇을 재정의해야할지 어느정도 컨셉을 잡아주는 역할로 사용됩니다.(상속개념)

package Anonymous;

public class Anonymous {

	//★★방법 1 : 필드에 익명자식 객체를 생성 
	Insect spider1 = new Insect(){
		
		String name = "무당거미";
		//거미줄을 치다.
		void cobweb(){
			System.out.println("사각형으로 거미줄을 친다.");
		}
		
		@Override
		void attack() {
			System.out.println(name + " 독을 발사한다.");
		}
	};
	
	//★★방법2 : 로컬변수의 초기값으로 대입
	void method1(){
		Insect spider2 = new Insect(){
			
			String name = "늑대거미";
			//거미줄을 치다.
			void cobweb(){
				System.out.println("육각형으로 거미줄을 친다.");
			}
			
			@Override
			void attack() {
				System.out.println(name + " 앞니로 문다.");
			}
		};
		
		//로컬변수이기 때문에 메서드에서 바로 사용
		spider2.attack();
	}
	
	//★★방법3 : 익명객체 매개변수로 대입
	void method2(Insect spider){
		spider.attack();
	}
	
	
}

그다음, 부모(곤충)객체 필드에 자식 익명객체를 바로 정의하여 초기값을 할당하고 있습니다.

또, method1을 보시면 해당 메서드 내부에서 지역적으로 익명객체를 생성하고, 바로 spider2.attack() 메서드를 호출하여 익명객체를 사용했습니다.

 

마지막으로 부모(곤충)객체를 매개변수 인자로 받게끔 하고, 받은 후 바로 spider.attack() 메서드를 호출하여 사용하도록 했습니다. 이제 위에서 정의한 Anonymous 클래스를 아래 main 함수에서 호출하도록 하면 아래와 같이 결과값이 나옵니다.

 

참고로!! 익명자식객체에서 새롭게 정의된 필드와 메소드는 자식객체 레벨에서만 사용되기 때문에 외부에서는 사용할 수 없습니다! 생각해보면 익명객체를 받아준 변수는 부모타입의 클래스이기 때문에 부모레벨에서 정의된 필드나 메서드만 사용이 가능합니다.

그래서 a.spider1.name, a.spider1.cobweb() 은 사용할 수 없고

a.spider1.attack() 메서드는 사용할 수 있는 것입니다.

package Anonymous;

public class AnonymousExample {

	public static void main(String[] args) {
		
		Anonymous a = new Anonymous();
		
		//방법 1 : 익명객체 필드 사용
		a.spider1.attack();

		//방법2 : 익명객체 로컬 변수 사용
		a.method1();
		
		//방법3 : 매개변수로 익명개체 사용
		a.method2(new Insect(){
			String name = "타란툴라";
			
			//거미줄을 치다.
			void cobweb(){
				System.out.println("그냥 마구잡이로 친다.");
			}
			
			@Override
			void attack() {
				System.out.println(name + " 공격 안하고 후퇴한다..");
			};
		});
		
		//익명객체 내부에서 새롭게 정의된 필드,메서드는 부모객체로 생성된 spider1에서 접근할 수 없음!!!
		a.spider1.name = "왕거미"; //익명객체에서 새롭게 정의된 필드 (접근불가)
		a.spider1.cobweb(); //익명객체에서 새롭게 정의된 메서드 (접근불가)
		a.spider1.attack(); //부모클래스 Insect에서 오버라이딩해서 재정의한 메서드 (접근가능)
	}

}

결과

무당거미 독을 발사한다.
늑대거미 앞니로 문다.
타란툴라 공격 안하고 후퇴한다..

 

반응형

 


익명객체(익명클래스) 컴파일시 만들어지는 $ 클래스

다들 아시겠지만, 코딩된 .java 파일은 자바 컴파일러를 통해 컴파일되고 .class 파일을 떨구게 됩니다.

보통 .java 파일이 .class 파일과 1:1관계를 맺지만 익명객체(익명클래스)가 사용된 .java 파일을 컴파일 하게되면 .class 파일 뿐만 아니라 $.class를 떨구게 됩니다.

만약 A.java라는 파일에서 익명객체를 정의했다면 A.class, A$1.class 이렇게 2개 클래스 파일이 생깁니다. 만약 익명객체를 2개 A.java 파일에 정의했다면 A.class, A$1.class, A$2.class 이렇게 3개 클래스 파일이 생깁니다.

 

규칙은 자바파일명${익명객체정의된순번}.class 으로 생각하시면 됩니다.

 

실제로 이렇게 클래스 파일이 생성되었는지 확인해보겠습니다.

프로젝트 우클릭 -> Properties -> Java Build Path -> Source 항목 -> 하단 경로..

 

프로젝트 우클릭
프로젝트 class 떨구는 경로 확인 (Defalut output folder)
JAVA_STUDY/bin/Anonymous 내 $ class파일 확인

 

내용을 보시면, Anonymous 클래스에서 익명객체 정의를 2번하였기 때문에  

Anonymous$1.class,  Anonymous$2.class 확인됩니다. 

또 AnonymousExample 예제에서 메서드 매개변수로 넘겨줄때 익명객체를 1번 정의하였으니 AnonymousExample$1.class가 있는게 확인됩니다.

 

오늘은 익명객체(클래스)에 대해서 알아보았습니다. 아울러 오늘 설명드린 방식은 부모/자식간 상속개념을 바탕으로 포스팅되었지만, 인터페이스를 통해서도 익명객체를 구현할 수 있습니다. 이를 익명 구현 객체라고 하는데 익명 구현 객체는 다음 편에서 다루도록 하겠습니다~!! 댓글과 좋아요. 감사합니다..ㅎㅎ

반응형