티스토리 뷰

리플렉션은 복잡한 특수 시스템을 개발할 때 필요한 강력한 기능이지만, 단점도 많다.
따라서 인스턴스 생성에만 쓰고, 이렇게 만든 인스턴스는 인터페이스나 상위 클래스로 참조해 사용하자.

 

🔎 리플렉션?

리플렉션이란, 구체적인 클래스 타입을 알지 못해도 그 클래스의 메소드, 타입, 변수들에 접근할 수 있도록 해주는 자바  API이다. 

즉, 구체적인 클래스 타입을 모를때 사용하는 방법을 리플렉션이라고 한다.

 

그런데, 내가 짠 코든데 내가 만든 클래스의 이름을 모르는 게 말이 되는 일인가? 의문이 들 수 있다.

하지만 가끔 어떤 타입의 클래스나 변수, 혹은 메소드를 사용해야 할지 모르는 경우가 생긴다.

예를 들어 변수의 값을 조건에 따라서 다르게 사용해야하는 경우라던가, 애플리케이션이 실행되고 나서 생성되는 클래스 같은 경우가 있다.

이런 경우에 리플렉션을 사용할 수 있다.

 

정리하자면, 리플렉션은 Class 객체가 주어지면 그 클래스의 생성자, 메서드, 필드에 해당하는 Constructor, Method, Field 인스턴스를 이용해 각각에 연결된 실제 생성자, 메서드, 필드를 조작할 수 있다. 

즉, 인스턴스들을 통해 해당 클래스의 인스턴스를 생성하거나, 메서드를 호출하거나, 필드에 접근할 수 있다.

 

하지만 리플렉션은 여러 단점이 존재한다.

 

👎 리플렉션의 단점

1. 컴파일타임 타입 검사가 주는 이점을 하나도 누릴 수 없다.

프로그램이 리플렉션 기능을 써서 존재하지 않는, 혹은 접근할 수 없는 메서드를 호출하려 시도하면 이에 대비하지 않은 이상 런타임 오류가 발생한다.

 

2. 코드가 지저분하고 장황해진다.

뒤에 나오는 예제 코드에서 리플렉션을 사용하면 얼마나 코드가 길어지고 지저분해지는지 확인할 수 있다.

 

3. 성능이 떨어진다.

리플렉션을 통한 메서드 호출은 일반 메서드 호출보다 훨씬 느리다

 

👍 그럼 어떻게 사용하지?

따라서 리플렉션은 아주 제한된 형태로만 사용해야 단점들을 피하고 이점만 취할 수 있다.

이 내용은 'Item 64. 객체는 인터페이스를 사용해 참조하라'와 이어진다.

리플렉션은 인스턴스 생성에만 쓰고, 이렇게 만든 인스턴스는 인터페이스나 상위 클래스로 참조해 사용하자.

 

/* 리플렉션으로 생성하고 인터페이스로 참조해 활용한다. */
public static void main(String[] args){
	// 클래스 이름을 Class 객체로 변환
    Class<? extends Set<String>> cl = null;
    try{
    	cl = (Class<? extends Set<String>>) //비검사 형변환
        	Class.forName(args[0]);
    }catch(ClassNotFoundException e){
    	fatalError("클래스를 찾을 수 없습니다.");
    }
    
    // 생성자를 얻는다.
    Constructor<? extends Set<String>> cons = null;
    try{
    	cons = cl.getDeclaredConstructor();
    } catch (NoSuchMethodException e){
    	fatalError("매개변수 없는 생성자를 찾을 수 없습니다.");
    }
    
    //집합의 인스턴스를 만든다.
    Set<String> s = null;
    try{
    	s = cons.newInstance();
    } catch (IllegalAccessException e){
    	fatalError("생성자에 접근할 수 없습니다.");
    } catch (InstantiationException e){
    	fatalError("클래스를 인스턴스화 할 수 없습니다.");
    } catch (InvocationTargetException e){
    	fatalError("생성자가 예외를 던졌습니다: "+e.getCause());
    } catch (ClassCastException e){
    	fatalError("Set을 구현하지 않은 클래스입니다.");
    }
    
    //생성한 집합을 사용한다.
    s.addAll(Arrays.asList(args).subList(1, args.length));
    System.out.println(s);
}

private static void fatalError(String msg){
	System.err.println(msg);
    System.exit(1);
}

위 코드에서는 리플렉션의 단점 두 가지를 찾을 수 있다.

1. 런타임에 총 여섯 가지나 되는 예외를 던질 수 있다.

이 모든 예외가 인스턴스를 리플렉션 없이 생성했다면 컴파일 타임에 잡아낼 수 있었을 예외들이다.

 

2. 클래스 이름만으로 인스턴스를 생성해내기 위해 무려 25줄이나 되는 코드를 작성했다.

리플렉션이 아니라면 생성자 호출 한 줄로 끝났을 일이었다. 

(자바 7부터 지원되는 ReflectiveOperationException을 사용하면 예외를 한 번에 잡을 수 있다.)

(얼마나 예외가 많았으면,,,, 하는 생각이 든다,,,,, )

 

❓ 그럼 리플렉션은 쓰지 말라는 건가?

리플렉션은 런타임에 존재하지 않을 수도 있는 다른 클래스, 메서드, 필드와의 의존성을 관리할 때 적합하다.(물론 이런 경우는 드물다)

이 기법은 버전이 여러 개 존재하는 외부 패키지를 다룰 때 유용하며,

가동할 수 있는 최소한의 환경(가장 오래된 버전)만을 지원하도록 컴파일한 후, 이후 버전의 클래스와 메서드 등은 리플렉션으로 접근하는 방식이다.

이렇게 하려면 접근하려는 새로운 클래스나 메서드가 런타임에 존재하지 않을 수 있다는 사실을 감안해야 한다.

즉, 같은 목적을 이룰 수 있는 대체 수단을 이용하거나 기능을 줄여 동작하는 등의 적절한 조치를 취해야 한다.

 

📚 정리하자!

리플렉션은 복잡한 특수 시스템을 개발할 때 필요한 강력한 기능이지만, 단점도 많다.
컴파일 타임에는 알 수 없는 클래스를 사용하는 프로그램을 작성한다면 리플렉션을 사용해야 할 것이다.
단, 되도록 객체 생성에만 사용하고, 생성한 객체를 이용할 때는 적절한 인터페이스나 컴파일타임에 알 수 있는 상위 클래스로 형 변환해 사용해야 한다.

 

 

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
글 보관함