반사 란 무엇이며 왜 유용합니까?


2123

리플렉션이란 무엇이며 왜 유용합니까?

특히 Java에 관심이 있지만 원칙이 모든 언어에서 동일하다고 가정합니다.


9
나를 위해 그것은 런타임에 클래스 이름을 얻고 해당 클래스의 객체를 만드는 방법입니다.
Nabin

64
이것은 인기있는 질문이기 때문에 반사 (주석없이)가 문제를 해결할 때 가장 마지막으로 사용해야하는 도구라고 지적하고 싶습니다. 나는 그것을 사용하고 그것을 좋아하지만 Java의 정적 타이핑의 모든 장점을 취소합니다. 필요한 경우 가능한 한 작은 영역 (하나의 방법 또는 하나의 클래스)으로 격리하십시오. 프로덕션 코드보다 테스트에서 사용하는 것이 더 허용됩니다. 주석을 사용하면 괜찮을 것입니다. 요점은 클래스 나 메소드 이름을 "문자열"로 지정하지 않는 것입니다.
Bill K


4
@BillK의 의견 외에도 리플렉션은 매우 강력하므로 마법이라고 부릅니다. 큰 힘에는 큰 책임이 따른다. 하고있는 일을 알고있는 경우에만 사용하십시오.
MC 황제

매니 폴드를 사용하여 반사와 관련된 많은 함정을 피할 수 있습니다 @Jailbreak. 개인 필드, 메소드 등에 대한 직접적이고 안전한 유형의 액세스를 제공 합니다. Java 컴파일러가 코드를 안전하게 확인하고 매니 폴드가 기본 리플렉션 액세스를 생성하도록하십시오. 자세히 알아보기 : manifold.systems/docs.html#type-safe-reflection
Scott

답변:


1716

이름 반영은 동일한 시스템 (또는 자체)에서 다른 코드를 검사 할 수있는 코드를 설명하는 데 사용됩니다.

예를 들어, Java에 알 수없는 유형의 객체가 있고 'doSomething'메소드가 있으면이를 호출하려고한다고 가정하십시오. Java의 정적 타이핑 시스템은 객체가 알려진 인터페이스를 준수하지 않는 한 실제로 이것을 지원하도록 설계되지 않았지만 리플렉션을 사용하면 코드에서 객체를보고 'doSomething'이라는 메소드가 있는지 확인한 다음 호출하면 호출 할 수 있습니다 고 싶어요.

따라서 Java로 코드 예제를 제공하려면 (문제의 객체가 foo라고 상상하십시오.)

Method method = foo.getClass().getMethod("doSomething", null);
method.invoke(foo, null);

Java에서 가장 일반적인 사용 사례는 주석을 사용한 사용법입니다. 예를 들어, JUnit 4는 리플렉션을 사용하여 @Test 주석으로 태그가 지정된 메소드를 클래스에서 살펴본 다음 단위 테스트를 실행할 때이를 호출합니다.

http://docs.oracle.com/javase/tutorial/reflect/index.html 에서 시작할 수있는 몇 가지 좋은 예가 있습니다.

마지막으로, 개념은 C #과 같이 리플렉션을 지원하는 다른 정적으로 유형이 지정된 언어와 거의 유사합니다. 동적으로 유형이 지정된 언어에서는 위에서 설명한 유스 케이스가 덜 필요합니다 (컴파일러는 객체에서 메소드를 호출 할 수 있기 때문에 객체가 없을 경우 런타임에 실패 함). 특정 방식으로 작업하는 것은 여전히 ​​흔합니다.

댓글에서 업데이트 :

시스템에서 코드를 검사하고 객체 유형을 볼 수있는 기능은 리플렉션이 아니라 유형 검사입니다. 리플렉션은 런타임에 내부 검사를 사용하여 수정하는 기능입니다. 일부 언어는 내부 검사를 지원하지만 리플렉션은 지원하지 않으므로 구별이 필요합니다. 그러한 예 중 하나는 C ++입니다.


32
이 줄에서 해당 null 매개 변수의 중요성을 설명해 주시겠습니까? Method method = foo.getClass (). getMethod ( "doSomething", null);
Krsna Chaitanya

54
널은 foo 메소드로 전달되는 매개 변수가 없음을 나타냅니다. 자세한 내용은 docs.oracle.com/javase/6/docs/api/java/lang/reflect/… , java.lang.Object ...)를 참조하십시오.
매트 셰퍼드

791
이것에는 많은 투표가 있기 때문에 정리하십시오. 시스템에서 코드를 검사하고 객체 유형을 볼 수있는 기능은 리플렉션이 아니라 유형 검사입니다. 리플렉션은 런타임에 내부 검사를 사용하여 수정하는 기능입니다. 일부 언어는 내부 검사를 지원하지만 리플렉션은 지원하지 않으므로 구별이 필요합니다. 그러한 예 중 하나는 C ++입니다.
bigtunacan

39
나는 리플렉션을 좋아하지만 코드를 제어 할 수 있다면이 답변에 지정된 리플렉션을 사용하는 것은 불필요하고 남용입니다 .Type Introspection (instanceof)과 강력한 유형을 사용해야합니다. 무언가를하기 위해 성찰이 아닌 다른 방법이 있다면, 그렇게해야합니다. 정적 인 형식의 언어를 사용하면 얻을 수있는 모든 이점을 잃기 때문에 리플렉션은 심각한 상심을 유발합니다. 필요한 경우 Spring과 같은 사전 패키지 솔루션 또는 필요한 반사를 완전히 캡슐화하는 것, 즉 다른 사람이 두통을 느끼도록 고려하십시오.
Bill K

6
@bigtunacan 어디서 그 정보를 얻었습니까? 런타임시 변경하는 기능뿐만 아니라 객체의 유형을 볼 수있는 기능을 설명하기 위해 Oracle 공식 Java 문서에서 사용되는 "반사"라는 용어가 있습니다. 대부분의 소위 "형 반성"관련 클래스를 언급하지 않기 (예 : Method, Constructor, Modifier, Field, Member, 기본적으로 분명히 외 모두 Class)에 내에있는 java.lang.*reflect*패키지. 아마도 "반사"라는 개념에는 "유형 내부 검사"와 런타임시 수정이 모두 포함되어 있습니까?
RestInPeace

246

리플렉션 은 런타임에 클래스, 메소드, 속성 등을 검사하고 동적으로 호출하는 언어의 기능입니다.

예를 들어, Java의 모든 객체에는 메소드가 있습니다.이 메소드 getClass()를 사용하면 컴파일 타임에 객체를 모르더라도 (예 : 객체를으로 선언 한 경우에도) 객체의 클래스를 확인할 Object수 있습니다. 와 같이 덜 동적 인 언어로 C++. 보다 고급으로 사용하면 메소드, 생성자 등을 나열하고 호출 할 수 있습니다.

리플렉션은 컴파일 타임에 모든 것을 "알지"않아도되는 프로그램을 작성할 수있게 해주므로 런타임에 서로 묶을 수 있기 때문에 더욱 역동적으로 만들 수 있기 때문에 중요합니다. 알려진 인터페이스에 대해 코드를 작성할 수 있지만 구성 파일에서 리플렉션을 사용하여 사용할 실제 클래스를 인스턴스화 할 수 있습니다.

많은 현대 프레임 워크가 바로 이런 이유로 반사를 광범위하게 사용합니다. 대부분의 다른 현대 언어도 리플렉션을 사용하며, 스크립트 언어 (예 : Python)에서는 언어의 일반적인 프로그래밍 모델에서보다 자연스럽게 느껴지므로 훨씬 더 밀접하게 통합되어 있습니다.


2
즉, 정규화 된 이름을 사용하여 인스턴스를 만들 수 있으며 컴파일러는 클래스 이름에 문자열 만 사용하기 때문에 이에 대해 불평하지 않습니다. 그런 다음 런타임에 해당 클래스가 없으면 예외가 발생합니다. 이 경우 컴파일러를 무시했습니다. 이것에 대한 특정 사용 사례를 알려주시겠습니까? 내가 그것을 선택할 때 나는 단지 그림을 그릴 수 없다.
Fernando Gabrieli

3
@FernandoGabrieli 리플렉션을 통해 런타임 오류를 쉽게 생성 할 수 있지만 런타임 예외 위험없이 리플렉션을 사용하는 것도 가능합니다. 내 대답에서 알 수 있듯이 리플렉션의 일반적인 용도는 라이브러리 또는 프레임 워크에 사용되며 , 응용 프로그램과 별도로 컴파일되기 때문에 컴파일 타임에 응용 프로그램의 구조 를 명시 적으로 알 수 없습니다 . "컨벤션 별 코드"를 사용하는 라이브러리는 리플렉션을 사용할 가능성이 있지만 반드시 마법 문자열을 사용하는 것은 아닙니다.
Liedman

C++런타임 유형 정보가 있습니다. RTTI
Ayxan

114

리플렉션을 가장 좋아하는 용도 중 하나는 아래 Java 덤프 방법입니다. 모든 객체를 매개 변수로 사용하고 Java 리플렉션 API를 사용하여 모든 필드 이름과 값을 인쇄합니다.

import java.lang.reflect.Array;
import java.lang.reflect.Field;

public static String dump(Object o, int callCount) {
    callCount++;
    StringBuffer tabs = new StringBuffer();
    for (int k = 0; k < callCount; k++) {
        tabs.append("\t");
    }
    StringBuffer buffer = new StringBuffer();
    Class oClass = o.getClass();
    if (oClass.isArray()) {
        buffer.append("\n");
        buffer.append(tabs.toString());
        buffer.append("[");
        for (int i = 0; i < Array.getLength(o); i++) {
            if (i < 0)
                buffer.append(",");
            Object value = Array.get(o, i);
            if (value.getClass().isPrimitive() ||
                    value.getClass() == java.lang.Long.class ||
                    value.getClass() == java.lang.String.class ||
                    value.getClass() == java.lang.Integer.class ||
                    value.getClass() == java.lang.Boolean.class
                    ) {
                buffer.append(value);
            } else {
                buffer.append(dump(value, callCount));
            }
        }
        buffer.append(tabs.toString());
        buffer.append("]\n");
    } else {
        buffer.append("\n");
        buffer.append(tabs.toString());
        buffer.append("{\n");
        while (oClass != null) {
            Field[] fields = oClass.getDeclaredFields();
            for (int i = 0; i < fields.length; i++) {
                buffer.append(tabs.toString());
                fields[i].setAccessible(true);
                buffer.append(fields[i].getName());
                buffer.append("=");
                try {
                    Object value = fields[i].get(o);
                    if (value != null) {
                        if (value.getClass().isPrimitive() ||
                                value.getClass() == java.lang.Long.class ||
                                value.getClass() == java.lang.String.class ||
                                value.getClass() == java.lang.Integer.class ||
                                value.getClass() == java.lang.Boolean.class
                                ) {
                            buffer.append(value);
                        } else {
                            buffer.append(dump(value, callCount));
                        }
                    }
                } catch (IllegalAccessException e) {
                    buffer.append(e.getMessage());
                }
                buffer.append("\n");
            }
            oClass = oClass.getSuperclass();
        }
        buffer.append(tabs.toString());
        buffer.append("}\n");
    }
    return buffer.toString();
}

8
Callcount는 무엇으로 설정해야합니까?
Tom

8
스레드를 실행할 때 스레드 "AWT-EventQueue-0"java.lang.StackOverflowError에 예외가 발생했습니다.
Tom

3
@Tom callCount은 0으로 설정해야합니다. 이 값은 각 출력 줄 앞에 몇 개의 탭이 있어야하는지 결정하는 데 사용됩니다. 덤프가 "하위 오브젝트"를 덤프해야 할 때마다 출력이 상위에 중첩 된 것으로 인쇄됩니다. 이 방법은 다른 방법으로 감쌀 때 유용합니다. 고려하십시오 printDump(Object obj){ System.out.println(dump(obj, 0)); }.
fny

1
확인되지 않은 재귀로 인해 순환 참조의 경우 java.lang.StackOverflowError가 작성 될 수 있습니다. buffer.append (dump (value, callCount))
Arnaud P

4
코드를 공개 도메인에 구체적으로 공개 할 수 있습니까?
stolsvik

84

반사의 사용

리플렉션은 일반적으로 Java 가상 머신에서 실행되는 애플리케이션의 런타임 동작을 검사하거나 수정해야하는 프로그램에서 사용됩니다. 이것은 비교적 고급 기능이며 언어의 기본 사항을 잘 알고있는 개발자 만 사용해야합니다. 이러한 경고를 염두에두고 리플렉션은 강력한 기술이므로 응용 프로그램에서 불가능한 작업을 수행 할 수 있습니다.

확장 성 기능

응용 프로그램은 정규화 된 이름을 사용하여 확장 성 개체 인스턴스를 만들어 외부 사용자 정의 클래스를 사용할 수 있습니다. 클래스 브라우저 및 비주얼 개발 환경 클래스 브라우저는 클래스 멤버를 열거 할 수 있어야합니다. 시각적 개발 환경에서는 개발자가 올바른 코드를 작성하는 데 도움이되도록 형식 정보를 반영하여 활용할 수 있습니다. 디버거 및 테스트 도구 디버거는 클래스의 개인 멤버를 검사 할 수 있어야합니다. 테스트 하네스는 리플렉션을 사용하여 클래스에 정의 된 검색 가능한 세트 API를 체계적으로 호출하여 테스트 스위트에서 높은 수준의 코드 커버리지를 보장 할 수 있습니다.

반사의 단점

반사는 강력하지만 무차별 적으로 사용해서는 안됩니다. 반사를 사용하지 않고 작업을 수행 할 수 있으면 사용하지 않는 것이 좋습니다. 리플렉션을 통해 코드에 액세스 할 때는 다음 사항을 염두에 두어야합니다.

  • 성능 오버 헤드

리플렉션에는 동적으로 분석되는 유형이 포함되므로 특정 Java 가상 머신 최적화를 수행 할 수 없습니다. 결과적으로, 반사 동작은 비 반사에 비해 성능이 느리고 성능에 민감한 응용 프로그램에서 자주 호출되는 코드 섹션에서는 피해야합니다.

  • 보안 제한

Reflection에는 보안 관리자에서 실행할 때 존재하지 않는 런타임 권한이 필요합니다. 이는 애플릿과 같이 제한된 보안 컨텍스트에서 실행되어야하는 코드에있어 중요한 고려 사항입니다.

  • 내부 노출

리플렉션을 사용하면 개인 필드 및 메소드에 액세스하는 것과 같이 비 반사 코드에서 불법적 인 작업을 코드에서 수행 할 수 있으므로 리플렉션을 사용하면 예기치 않은 부작용이 발생하여 코드가 제대로 작동하지 않아 이식성이 떨어질 수 있습니다. 반사 코드는 추상화를 손상 시키므로 플랫폼을 업그레이드하면 동작이 변경 될 수 있습니다.

출처 : Reflection API


44

리플렉션은 응용 프로그램이나 프레임 워크가 아직 작성되지 않았을 수도있는 코드로 작동 할 수 있도록하는 핵심 메커니즘입니다!

일반적인 web.xml 파일을 예로 들어 보겠습니다. 여기에는 중첩 된 서블릿 클래스 요소가 포함 된 서블릿 요소 목록이 포함됩니다. 서블릿 컨테이너는 web.xml 파일을 처리하고 리플렉션을 통해 각 서블릿 클래스의 새 인스턴스를 새로 만듭니다.

또 다른 예는 JAXP ( Java API for XML Parsing ) 입니다. XML 파서 공급자가 잘 알려진 시스템 속성을 통해 '플러그인'되는 경우 리플렉션을 통해 새 인스턴스를 구성하는 데 사용됩니다.

마지막으로 가장 포괄적 인 예는 리플렉션을 사용하여 Bean을 작성하고 프록시를 많이 사용하는 Spring 입니다.


36

모든 언어가 리플렉션을 지원하는 것은 아니지만 원칙을 지원하는 언어는 원칙이 동일합니다.

리플렉션은 프로그램 구조를 "반영"하는 능력입니다. 또는 더 구체적인. 가지고있는 객체와 클래스를보고 프로그래밍 방식으로 메소드, 필드 및 인터페이스에 대한 정보를 얻습니다. 주석과 같은 것을 볼 수도 있습니다.

많은 상황에서 유용합니다. 어디서나 코드에 클래스를 동적으로 연결할 수 있기를 원합니다. 많은 객체 관계형 맵퍼는 리플렉션을 사용하여 어떤 객체를 사용할지 미리 알지 않고도 데이터베이스에서 객체를 인스턴스화 할 수 있습니다. 플러그인 아키텍처는 리플렉션이 유용한 또 다른 장소입니다. 동적으로 코드를로드하고 플러그인으로 사용하기에 적합한 인터페이스를 구현하는 유형이 있는지 판별하는 것이 이러한 상황에서 중요합니다.


DB에있는 데이터를 기반으로 객체를 인스턴스화해야합니다. 나는 이것이 당신이 말하는 것이라고 믿습니다. 샘플 코드는 많은 도움이 될 것입니다. 미리 감사드립니다.
Atom

34

리플렉션은 구현에 대한 사전 지식없이 런타임에 새로운 객체의 인스턴스화, 메소드 호출 및 클래스 변수에 대한 작업 가져 오기 / 설정을 동적으로 허용합니다.

Class myObjectClass = MyObject.class;
Method[] method = myObjectClass.getMethods();

//Here the method takes a string parameter if there is no param, put null.
Method method = aClass.getMethod("method_name", String.class); 

Object returnValue = method.invoke(null, "parameter-value1");

위의 예에서 null 매개 변수는 메서드를 호출하려는 객체입니다. 메소드가 정적이면 널을 제공합니다. 메소드가 정적이 아닌 경우, 호출하는 동안 널 대신 유효한 MyObject 인스턴스를 제공해야합니다.

리플렉션을 사용하면 클래스의 비공개 멤버 / 방법에 액세스 할 수 있습니다.

public class A{

  private String str= null;

  public A(String str) {
  this.str= str;
  }
}

.

A obj= new A("Some value");

Field privateStringField = A.class.getDeclaredField("privateString");

//Turn off access check for this field
privateStringField.setAccessible(true);

String fieldValue = (String) privateStringField.get(obj);
System.out.println("fieldValue = " + fieldValue);
  • 클래스를 검사하기 위해 (또한 내성이라고도 함) 리플렉션 패키지 ( java.lang.reflect) 를 가져올 필요가 없습니다 . 를 통해 클래스 메타 데이터에 액세스 할 수 있습니다 java.lang.Class.

리플렉션은 매우 강력한 API이지만 런타임에 모든 유형을 확인하므로 초과 사용하면 응용 프로그램 속도가 느려질 수 있습니다.


21

Java Reflection은 매우 강력하며 매우 유용 할 수 있습니다. Java Reflection을 사용하면 컴파일 타임에 클래스, 메소드 등의 이름을 몰라도 런타임에 클래스, 인터페이스, 필드 및 메소드를 검사 할 수 있습니다. 리플렉션을 사용하여 새 객체인스턴스화하고, 메소드를 호출하고, 필드 값을 가져 오거나 설정할 수도 있습니다.

리플렉션 사용의 모습을 보여주는 빠른 Java 리플렉션 예제 :

Method[] methods = MyObject.class.getMethods();

    for(Method method : methods){
        System.out.println("method = " + method.getName());
    }

이 예제는 MyObject라는 클래스에서 Class 객체를 가져옵니다. 예제는 클래스 객체를 사용하여 해당 클래스의 메소드 목록을 가져 와서 메소드를 반복하고 이름을 인쇄합니다.

이 모든 것이 어떻게 작동하는지 정확히 여기에 설명되어 있습니다.

편집 : 거의 1 년 후 리플렉션을 읽는 동안 리플렉션을 더 많이 사용하지 않으므로이 답변을 편집하고 있습니다.

  • Spring은 다음과 같은 Bean 구성을 사용합니다.


<bean id="someID" class="com.example.Foo">
    <property name="someField" value="someValue" />
</bean>

Spring 컨텍스트가이 <bean> 요소를 처리 할 때 "com.example.Foo"인수와 함께 Class.forName (String)을 사용하여 해당 클래스를 인스턴스화합니다.

그런 다음 다시 반사를 사용하여 <property> 요소에 대한 적절한 설정자를 가져오고 해당 값을 지정된 값으로 설정합니다.

  • Junit은 특히 Private / Protected 메소드 테스트에 Reflection을 사용합니다.

개인 메소드의 경우

Method method = targetClass.getDeclaredMethod(methodName, argClasses);
method.setAccessible(true);
return method.invoke(targetObject, argObjects);

개인 필드의 경우

Field field = targetClass.getDeclaredField(fieldName);
field.setAccessible(true);
field.set(object, value);

21

예:

예를 들어 API 메소드를 사용하여 얻은 객체를 애플리케이션에 제공하는 원격 애플리케이션을 예로 들어 보겠습니다. 이제 객체를 기반으로 일종의 계산을 수행해야 할 수도 있습니다.

공급자는 객체가 3 가지 유형일 수 있으며 어떤 유형의 객체를 기반으로 계산을 수행해야하는지 보장합니다.

따라서 각각 다른 로직을 포함하는 3 개의 클래스로 구현할 수 있습니다. 물론 런타임에 객체 정보를 사용할 수 있으므로 정적으로 코드를 작성하여 계산을 수행 할 수 없으므로 리플렉션을 사용하여 계산을 수행하는 데 필요한 클래스의 객체를 인스턴스화 할 수 있습니다. 공급자로부터받은 개체입니다.


비슷한 개념이 필요합니다. 리플렉션 개념을 처음
Atom

2
혼란 스러워요 : instanceof런타임에 객체 유형을 결정 하는 데 사용할 수 없습니까?
ndm13

19

반사에 대한 간단한 예. 체스 게임에서는 런타임에 사용자가 무엇을 움직 일지 알 수 없습니다. 리플렉션은 런타임에 이미 구현 된 메소드를 호출하는 데 사용할 수 있습니다.

public class Test {

    public void firstMoveChoice(){
        System.out.println("First Move");
    } 
    public void secondMOveChoice(){
        System.out.println("Second Move");
    }
    public void thirdMoveChoice(){
        System.out.println("Third Move");
    }

    public static void main(String[] args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { 
        Test test = new Test();
        Method[] method = test.getClass().getMethods();
        //firstMoveChoice
        method[0].invoke(test, null);
        //secondMoveChoice
        method[1].invoke(test, null);
        //thirdMoveChoice
        method[2].invoke(test, null);
    }

}

18

리플렉션 은 런타임 에 메서드, 클래스, 인터페이스 의 동작을 검사하거나 수정하는 데 사용되는 API입니다 .

  1. 반영에 필요한 클래스는 아래에 제공됩니다 java.lang.reflect package.
  2. 리플렉션은 객체가 속한 클래스와 객체를 사용하여 실행할 수있는 클래스의 메소드에 대한 정보를 제공합니다.
  3. 리플렉션을 통해 사용 된 액세스 지정자와 상관없이 런타임에 메소드를 호출 할 수 있습니다.

java.langjava.lang.reflect패키지는 자바 반사하기위한 클래스를 제공합니다.

리플렉션 을 사용하여 다음에 대한 정보를 얻을 수 있습니다.

  1. 클래스getClass()방법은 객체가 속한 클래스의 이름을 가져 오는 데 사용됩니다.

  2. 생성자getConstructors() 방법은 객체가 속한 클래스의 public 생성자를 가져 오는 데 사용됩니다.

  3. 방법getMethods() 방법은 객체가 속한 클래스의 public 메소드를 가져 오는 데 사용됩니다.

반사 API는 주로 사용된다 :

IDE (Integrated Development Environment) (예 : Eclipse, MyEclipse, NetBeans 등)
디버거 및 테스트 도구 등

리플렉션 사용의 장점 :

확장 성 기능 : 응용 프로그램은 정규화 된 이름을 사용하여 확장 성 개체 인스턴스를 만들어 외부 사용자 정의 클래스를 사용할 수 있습니다.

디버깅 및 테스트 도구 : 디버거는 리플렉션 속성을 사용하여 클래스의 개인 멤버를 검사합니다.

단점 :

성능 오버 헤드 : 반사 작업은 비 반사 작업보다 성능이 느리므로 성능에 민감한 응용 프로그램에서 자주 호출되는 코드 섹션에서는 피해야합니다.

내부 노출 : 반사 코드는 추상화를 손상 시키므로 플랫폼 업그레이드에 따라 동작이 변경 될 수 있습니다.

참조 : 자바 리플렉션 javarevisited.blogspot.in


4
단점은 " 리팩토링을 깨는 것 "이라고 덧붙 입니다. 저에게는 그것이 가능한 한 반사를 피하는 주된 이유입니다.
SantiBailors

따라서 (예를 들어) 우리가 가지고있는 클래스를 검사 할 수 있습니다 (인스턴스가 있는지 여부에 관계없이)? 즉, 메소드 또는 생성자를 가져 와서 새 인스턴스를 작성하거나 호출하는 데 사용합니다. 동작이 이미 있지만 코드가 다른 경우 왜 "프로그램 동작 변경"이라고 말합니까? 왜 "반사"라고 불리는가? 감사합니다
페르난도 가브리엘리

15

내 이해에 따라 :

리플렉션을 사용하면 프로그래머가 프로그램의 엔터티에 동적으로 액세스 할 수 있습니다. 즉, 프로그래머가 클래스 나 클래스의 메서드를 모르는 경우 응용 프로그램을 코딩하는 동안 리플렉션을 사용하여 런타임에 이러한 클래스를 동적으로 사용할 수 있습니다.

클래스 이름이 자주 변경되는 시나리오에서 자주 사용됩니다. 이러한 상황이 발생하면 프로그래머가 응용 프로그램을 다시 작성하고 클래스 이름을 반복해서 변경하기가 복잡합니다.

대신 리플렉션을 사용하면 클래스 이름 변경 가능성에 대해 걱정할 필요가 있습니다.


15

리플렉션은 프로그램의 런타임 정보에 액세스하여 동작을 수정할 수있는 기능 세트입니다 (일부 제한 사항이 있음).

프로그램의 메타 정보에 따라 런타임 동작을 변경할 수 있기 때문에 유용합니다. 즉, 함수의 리턴 유형을 확인하고 상황 처리 방식을 변경할 수 있습니다.

예를 들어 C #에서는 런타임에 어셈블리 (.dll)를로드하여 클래스를 탐색하고 찾은 내용에 따라 작업을 수행 할 수 있습니다. 또한 런타임에 클래스의 인스턴스를 만들고 메소드 등을 호출 할 수 있습니다.

어디에서 유용 할 수 있습니까? 매번 유용하지는 않지만 구체적인 상황에 유용합니다. 예를 들어, 로깅 목적으로 클래스 이름을 가져오고 구성 파일에 지정된 내용에 따라 이벤트 핸들러를 동적으로 작성하는 데 사용할 수 있습니다.


11

나열된 모든 항목에 포인트를 추가하고 싶습니다.

Reflection API 를 사용하면 toString()모든 객체에 대한 범용 메소드를 작성할 수 있습니다 .

디버깅에 유용합니다.

다음은 몇 가지 예입니다.

class ObjectAnalyzer {

   private ArrayList<Object> visited = new ArrayList<Object>();

   /**
    * Converts an object to a string representation that lists all fields.
    * @param obj an object
    * @return a string with the object's class name and all field names and
    * values
    */
   public String toString(Object obj) {
      if (obj == null) return "null";
      if (visited.contains(obj)) return "...";
      visited.add(obj);
      Class cl = obj.getClass();
      if (cl == String.class) return (String) obj;
      if (cl.isArray()) {
         String r = cl.getComponentType() + "[]{";
         for (int i = 0; i < Array.getLength(obj); i++) {
            if (i > 0) r += ",";
            Object val = Array.get(obj, i);
            if (cl.getComponentType().isPrimitive()) r += val;
            else r += toString(val);
         }
         return r + "}";
      }

      String r = cl.getName();
      // inspect the fields of this class and all superclasses
      do {
         r += "[";
         Field[] fields = cl.getDeclaredFields();
         AccessibleObject.setAccessible(fields, true);
         // get the names and values of all fields
         for (Field f : fields) {
            if (!Modifier.isStatic(f.getModifiers())) {
               if (!r.endsWith("[")) r += ",";
               r += f.getName() + "=";
               try {
                  Class t = f.getType();
                  Object val = f.get(obj);
                  if (t.isPrimitive()) r += val;
                  else r += toString(val);
               } catch (Exception e) {
                  e.printStackTrace();
               }
            }
         }
         r += "]";
         cl = cl.getSuperclass();
      } while (cl != null);

      return r;
   }    
}

11

반사는 물체가 자신의 모습을 볼 수있게하는 것입니다. 이 주장은 성찰과 관련이없는 것 같습니다. 실제로 이것은 "자가 식별"기능입니다.

리플렉션 자체는 Java 및 C #과 같은 자체 지식 및 자체 감지 기능이없는 언어의 단어입니다. 그들에게는 자기 지식의 능력이 없기 때문에, 그것이 어떻게 보이는지 관찰하고 싶을 때, 우리는 그것이 어떻게 보이는지에 대해 또 다른 것을 고려해야합니다. Ruby 및 Python과 같은 탁월한 동적 언어는 다른 개인의 도움 없이도 자신의 언어를 반영 할 수 있습니다. Java의 객체는 리플렉션 클래스의 객체 인 미러가없는 경우의 모습을 인식 할 수 없지만 Python의 객체는 미러없이이를 인식 할 수 있습니다. 그래서 우리는 Java로 리플렉션해야합니다.


type (), isinstance (), callable (), dir () 및 getattr (). .... 이것은
파이 토닉

9

자바 문서 페이지에서

java.lang.reflect패키지는 클래스와 객체에 대한 반사 정보를 얻기위한 클래스와 인터페이스를 제공합니다. 리플렉션을 사용하면로드 된 클래스의 필드, 메서드 및 생성자에 대한 정보에 프로그래밍 방식으로 액세스하고 보안 제한 내에서 기본 필드에서 작동하는 반사 된 필드, 메서드 및 생성자를 사용할 수 있습니다.

AccessibleObject필요한 경우 액세스 점검을 억제 할 ReflectPermission수 있습니다.

와 함께,이 패키지의 클래스는, java.lang.Class같은 디버거, 통역, 개체 검사, 클래스 브라우저 및 같은 서비스와 같은 응용 프로그램을 수용 Object Serialization하고 JavaBeans대상 객체의 public 멤버 중 하나에 그 필요성 액세스 (실행시의 클래스에 근거한다) 또는 회원에 의해 선언 주어진 수업

다음과 같은 기능이 포함되어 있습니다.

  1. 클래스 객체 얻기
  2. 클래스의 속성 (필드, 메소드, 생성자) 검사
  3. 필드 값 설정 및 가져 오기
  4. 메소드 호출
  5. 객체의 새 인스턴스를 만듭니다.

클래스 에 의해 노출 된 메소드에 대한 이 문서 링크를 살펴보십시오 Class.

기사 (Dennis Sosnoski, Sosnoski Software Solutions, Inc. 사장) 및이 기사 (security-explorations pdf)에서 :

Reflection을 사용하는 것보다 상당한 단점이 있습니다.

리플렉션 사용자 :

  1. 프로그램 구성 요소를 동적으로 연결하는 매우 다양한 방법을 제공합니다.
  2. 매우 일반적인 방식으로 객체와 함께 작동하는 라이브러리를 만드는 데 유용합니다

반사의 단점 :

  1. 필드 및 메소드 액세스에 사용될 때 리플렉션은 직접 코드보다 훨씬 느립니다.
  2. 실제로 코드 내부에서 일어나는 일을 모호하게 할 수 있습니다
  3. 유지 보수 문제를 일으킬 수있는 소스 코드를 무시
  4. 리플렉션 코드는 해당 직접 코드보다 복잡합니다.
  5. 데이터 액세스 보호 및 유형 안전과 같은 주요 Java 보안 제약 조건을 위반할 수 있습니다.

일반적인 학대 :

  1. 제한된 클래스 로딩
  2. 제한된 클래스의 생성자, 메소드 또는 필드에 대한 참조 얻기
  3. 새로운 객체 인스턴스 생성, 메소드 호출, 제한된 클래스의 필드 값 가져 오기 또는 설정

리플렉션 기능 남용에 관한이 SE 질문을 살펴보십시오.

Java에서 개인 필드를 읽으려면 어떻게합니까?

요약:

시스템 코드 내에서 수행 된 기능을 안전하지 않게 사용하면 Java 보안 모드가 손상 될 수 있습니다 . 이 기능을 조금만 사용하십시오


리플렉션의 성능 문제를 피하는 방법은 Woozle클래스가 시작시 다른 클래스를 검사하여 정적 RegisterAsWoozleHelper()메소드 가있는 클래스를 확인 하고 콜백으로 찾은 모든 메소드를 호출하여 Woozle자신에 대해 알 수 있도록하는 것입니다. 데이터를 직렬화 해제하는 동안 Reflection을 사용해야합니다.
supercat

9

이름 자체가 제안하는 것처럼 런타임에 인스턴스를 동적으로 생성하는 메소드를 호출하는 기능을 제공하는 것 외에도 클래스 메소드 등의 예를 반영합니다.

실제로 코드를 몰라도 서비스를 호출하기 위해 많은 프레임 워크와 응용 프로그램에서 사용됩니다.


7

리플렉션을 사용하면보다 일반적인 코드를 작성할 수 있습니다. 런타임에 객체를 생성하고 런타임에 메소드를 호출 할 수 있습니다. 따라서 프로그램을 고도로 매개 변수화 할 수 있습니다. 또한 객체와 클래스를 조사하여 외부 세계에 노출 된 변수와 메소드를 감지 할 수 있습니다.


6

Reflection많은 용도가 있습니다. 내가 더 익숙한 것은 즉석에서 코드를 만들 수 있다는 것입니다.

IE : 동적 클래스, 함수, 생성자-모든 데이터 기반 (xml / array / sql results / hardcoded / etc.)


1
이 결과는 SQL 결과 나 XML 파일 등에서 생성 된 코드의 특이한 예를 하나만 제공하면 훨씬 더 나을 것입니다.
ThisClark

문제 없어요. 데이터베이스에서 가져온 XML을 기반으로 인터페이스를 동적으로 생성하는 Windows 응용 프로그램에서 리플렉션을 사용했습니다.
Ess Kay

기본적으로 사용자에게 보고서를 보여주는 클래스가 있습니다. 이 보고서에는 Date (from to) id 등의 매개 변수가 있습니다. 이 정보는 xml에 저장됩니다. 먼저 보고서를 선택했습니다. 선택한 보고서에 따라 양식이 xml을 가져옵니다. XML이 검색되면 리플렉션을 사용하여 리플렉션 유형을 기반으로 필드가있는 클래스를 만듭니다. 다른 보고서로 변경하면 슬레이트가 깨끗하게 지워지고 XML을 기반으로 새 필드가 생성됩니다. 따라서 본질적으로 반사에 기반한 동적 형태입니다. 나는 또한 다른 방법으로도 사용했지만 이것은 도움이되는 희망이어야합니다.
Ess Kay

3

이 질문에 예를 들어 대답하고 싶습니다. 우선 모든 Hibernate프로젝트는 실행중인 애플리케이션과 지속성 저장소 사이의 틈을 메우기위한 명령문 Reflection API을 생성 하는 데 사용 합니다 CRUD. 도메인에서 상황이 변경되면 Hibernate데이터 저장소에 데이터를 유지하고 그 반대로도 유지해야합니다.

또는 작동합니다 Lombok Project. 컴파일 타임에 코드를 삽입하여 도메인 클래스에 코드를 삽입합니다. (게터와 세터에게는 괜찮다고 생각합니다)

Hibernatereflection응용 프로그램의 빌드 프로세스에 최소한의 영향을 미치기 때문에 선택했습니다 .

그리고 Java 7부터는 다음 MethodHandles과 같이 작동합니다 Reflection API. 프로젝트에서 로거와 함께 작업하기 위해 다음 코드를 복사하여 붙여 넣습니다.

Logger LOGGER = Logger.getLogger(MethodHandles.lookup().lookupClass().getName());

이 경우 오타를 만들기가 어렵 기 때문입니다.


3

예를 들어 설명하는 것이 가장 좋았지 만 그 대답 중 어느 것도 그렇게하지 않는 것 같습니다 ...

리플렉션 사용의 실제 예는 Java로 작성된 Java Language Server 또는 PHP로 작성된 PHP Language Server입니다. Language Server는 자동 완성, 정의로 건너 뛰기, 컨텍스트 도움말, 힌트 유형 등과 같은 IDE 기능을 제공합니다. 입력 할 때 가능한 모든 일치 항목을 표시하기 위해 모든 태그 이름 (자동 완성 할 수있는 단어)을 갖기 위해 언어 서버는 문서 블록 및 개인 멤버를 포함하여 클래스에 대한 모든 것을 검사해야합니다. 이를 위해서는 상기 클래스의 반영이 필요하다.

다른 예는 개인용 메소드의 단위 테스트입니다. 이를 수행하는 한 가지 방법은 테스트 설정 단계에서 리플렉션을 작성하고 메소드 범위를 공용으로 변경하는 것입니다. 물론 개인 메소드는 직접 테스트해서는 안되지만 요점은 아닙니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.