Java - Reflection

INJE YUN·2020년 12월 29일

개요

컴퓨터 과학에서 리플렉션(reflection)은 컴퓨터 프로그램에서 런타임 시점에 사용되는 자신의 구조와 행위를 관리(type introspection)하고 수정할 수 있는 프로세스를 의미한다.

Java의 Reflection 기능은 주로 JVM에서 실행되는 어플리케이션의 런타임 동작을 검사하거나 수정하는 기능이 필요한 프로그램이나 코드 분석 도구 및 의존성 주입 프레임워크에서 일반적으로 사용되었다.

기능

  • Discover and modify source-code constructions (such as code blocks, classes, methods, protocols, etc.) as first-class objects at runtime.

    런타임에 소스 코드 구성(코드 블록, 클래스, 메서드, 프로토콜 등)을 1급 개체로 검색하고 수정한다.

  • Convert a string matching the symbolic name of a class or function into a reference to or invocation of that class or function.

    클래스 또는 함수의 기호 이름과 일치하는 문자열을 해당 클래스 또는 함수의 참조 또는 호출로 변환한다.

  • Evaluate a string as if it were a source-code statement at runtime.

    런타임에 문자열을 소스 코드 문인 것처럼 평가한다.

  • Create a new interpreter for the language's bytecode to give a new meaning or purpose for a programming construct.

    언어의 바이트 코드에 대한 새 인터프리터를 만들어 프로그래밍 구조에 대한 새로운 의미 또는 목적을 제공한다.

단점

  • Performance Overhead

    리플렉션에는 런타임중에 동적으로 진행되는 로직이 포함되기 때문에 특정 JVM 최적화를 수행할 수 없어, 리플렉션을 통한 메소드 호출은 일반 메소드 호출보다 느리다.

  • Low Readability

    String a = new String("a");
    
    // 일반적인 객체 생성
    String b = new String(a);
    
    // 리플렉션을 통한 객체 생성
    Constructor cons = null;
    String c = null;
    try {
    	cons = a.getClass().getDeclaredConstructor();
    	c = (String) cons.newInstance(a);
    } catch (NoSuchMethodException e) {
    	e.printStackTrace();
    } catch (IllegalAccessException e) {
    	e.printStackTrace();
    } catch (InstantiationException e) {
    	e.printStackTrace();
    } catch (InvocationTargetException e) {
    	e.printStackTrace();
    }
  • Exposure of Internals

    리플렉션을 사용하면 private 필드 및 메소드에 접근하는 작업을 수행할 수 있어, 예기치않은 부작용이 발생하여 코드가 제대로 작동하지 않고 이식성이 손상될 수 있다.

  • Readability 리플렉션을 쓰면 코드가 매우 번거로워지고 양도 많아진다. 코드를 작성하기도 귀찮아지고 읽기도 힘들다.

  • Type Checking 예외에 대한 검사를 포함하여, 모든 컴파일 시점의 타입 검사를 포기해야한다. ****존재하지 않거나 접근할 수 없는 메소드를 리플렉션을 써서 호출했을 때 사전 조치를 제대로 하지 않았다면 실행시점에 오류가 발생할 것이다.

선례

  • Java Serialization

    객체를 직렬화(Serialization) 해야 할 경우 Serializable 인터페이스를 구현한다. 그리고, 직렬화된 객체를 읽기 위해서는 java.io.ObjectInputStream 클래스의 readObject() 메서드를 이용한다. 필요에 따라 Serializable 인터페이스를 구현한 클래스가 readObject() 메서드를 구현할 수도 있다. 이때, java.io.ObjectInputStream 클래스의 readObject() 메서드는 내부적으로 Reflection을 이용하여 직렬화된 객체의 readObject() 메서드를 호출한다.

    https://kmongcom.files.wordpress.com/2014/03/a4.png?w=584

  • Spring Bean

    Spring의 Bean은 어플리케이션이 실행된 후 런타임에 객체가 호출될 때 동적으로 객체의 인스턴스를 생성하는데, 이때 Spring Container의 BeanFactory에서 리플렉션을 사용한다.

  • 등등

    // Factory 클래스 개발
    
    // 새로운 게임이 추가될 경우 if문을 추가해야한다.
    public class GameFactory {
    	public Object getInstance(String name) {
    		if (name.equals("lol") {
    			return new LeageOfLegend();
    		} else if (name.equals("battle ground")) {
    			return new BattleGround();
    		}
    		return null;
    	}
    }
    
    // 새로운 게임이 추가되어도 수정이 필요하지 않다.
    public class GameFactory {
    	public Object getInstance(String className) {
    		Class cls = Class.forName(className);
    		Constructor constructor = cls.getConstructor();
    		return constructor.newInstance();
    	}
    }

결론

리플렉션은 복잡한 특수 시스템을 개발할 때 필요한 강력한 기능이지만, 단점도 많다. 하지만 매우 제한된 형태로만 사용한다면 많은 이점을 얻을 수 있지만, 비용은 거의 들지 않는다. 예를들어 컴파일 시간 동안에 알 수 없는 클래스를 사용하는 프로그램을 작성하게 된다면, 객체 생성에서만 리플렉션을 사용하고, 인터페이스나 컴파일 시간 동안에 알 수 있는 상위 클래스로 캐스팅해 사용하면 된다.

출처

profile
신입 개발자입니다.

0개의 댓글