저는 Java를 처음 사용합니다. 내 연구를 통해 리플렉션이 클래스와 메서드를 호출하고 어떤 메서드가 구현되었는지 알기 위해 사용된다는 것을 읽었습니다.
리플렉션을 언제 사용해야합니까? 리플렉션을 사용하고 객체를 인스턴스화하는 것과 전통적인 방법을 호출하는 것의 차이점은 무엇입니까?
저는 Java를 처음 사용합니다. 내 연구를 통해 리플렉션이 클래스와 메서드를 호출하고 어떤 메서드가 구현되었는지 알기 위해 사용된다는 것을 읽었습니다.
리플렉션을 언제 사용해야합니까? 리플렉션을 사용하고 객체를 인스턴스화하는 것과 전통적인 방법을 호출하는 것의 차이점은 무엇입니까?
답변:
리플렉션은 이름으로 메서드를 호출하는 것보다 훨씬 느립니다. 미리 컴파일 된 주소와 상수를 사용하는 대신 바이트 코드의 메타 데이터를 검사해야하기 때문입니다.
리플렉션도 더욱 강력합니다. protected
또는 final
멤버 의 정의를 검색하고 , 보호를 제거하고 마치 변경 가능하다고 선언 된 것처럼 조작 할 수 있습니다! 분명히 이것은 언어가 일반적으로 프로그램에 대해 보장하는 많은 보증을 파괴하며 매우 위험 할 수 있습니다.
그리고 이것은 그것을 언제 사용 해야하는지 거의 설명합니다. 보통은하지 마십시오. 메소드를 호출하려면 호출하십시오. 멤버를 변경하려면 컴파일 백으로 돌아 가지 않고 변경 가능하도록 선언하십시오.
실제 리플렉션 사용 중 하나는 사용자 정의 클래스와 상호 운용해야하는 프레임 워크를 작성할 때 프레임 워크 작성자가 멤버 (또는 클래스)가 무엇인지 알지 못하는 경우입니다. 리플렉션을 통해 학생들은 미리 알지 않고도 모든 수업을 처리 할 수 있습니다. 예를 들어, 복잡한 측면 지향 라이브러리를 반영하지 않고 작성할 수 있다고 생각하지 않습니다.
또 다른 예로, JUnit은 사소한 리플렉션을 사용했습니다. 클래스의 모든 메소드를 열거하고 호출 된 모든 testXXX
메소드가 테스트 메소드 라고 가정하고 해당 메소드 만 실행합니다. 그러나 이제는 주석 대신에 더 잘 수행 할 수 있으며 실제로 JUnit 4는 주석 대신 크게 이동했습니다.
나는 한 번 당신과 같았습니다. 나는 반사에 대해 많이 몰랐습니다. 여전히 모르겠습니다. 그러나 나는 그것을 한 번 사용했습니다.
나는 두 개의 내부 클래스가있는 클래스를 가지고 있었고 각 클래스에는 많은 메소드가있었습니다.
내부 클래스의 모든 메서드를 호출해야했으며 수동으로 호출하면 너무 많은 작업이 수행되었습니다.
리플렉션을 사용하여 메서드 자체 수 대신 2-3 줄의 코드로 이러한 모든 메서드를 호출 할 수 있습니다.
리플렉션 사용을 세 그룹으로 그룹화했습니다.
리플렉션을 통해 프로그램은 존재하지 않을 수있는 코드로 작업 할 수 있으며 신뢰할 수있는 방식으로 코드를 작성할 수 있습니다.
"정상 코드"에는 스 니펫 URLConnection c = null
이있어 클래스 로더가이 클래스를로드하는 동안 클래스 로더가 URLConnection 클래스를로드하여 ClassNotFound 예외를 발생시키고 종료하는 것과 같은 스 니펫 이 있습니다.
리플렉션을 사용하면 문자열 형식으로 이름을 기반으로 클래스를로드하고 클래스에 종속되는 실제 클래스를 시작하기 전에 다양한 속성 (컨트롤 외부의 여러 버전에 유용한)에 대해 테스트 할 수 있습니다. 일반적인 예는 Java 프로그램을 다른 플랫폼에는없는 OS X에서 고유하게 보이도록하는 데 사용되는 OS X 특정 코드입니다.
기본적으로 리플렉션은 프로그램 코드를 데이터로 사용하는 것을 의미합니다.
따라서 프로그램 코드가 유용한 데이터 소스 인 경우 리플렉션을 사용하는 것이 좋습니다. (그러나 절충점이 있기 때문에 항상 좋은 생각은 아닙니다.)
예를 들어 간단한 클래스를 생각해보십시오.
public class Foo {
public int value;
public string anotherValue;
}
XML을 생성하려고합니다. XML을 생성하는 코드를 작성할 수 있습니다.
public XmlNode generateXml(Foo foo) {
XmlElement root = new XmlElement("Foo");
XmlElement valueElement = new XmlElement("value");
valueElement.add(new XmlText(Integer.toString(foo.value)));
root.add(valueElement);
XmlElement anotherValueElement = new XmlElement("anotherValue");
anotherValueElement.add(new XmlText(foo.anotherValue));
root.add(anotherValueElement);
return root;
}
그러나 이것은 많은 상용구 코드이며 클래스를 변경할 때마다 코드를 업데이트해야합니다. 실제로이 코드의 기능을 설명 할 수 있습니다
이것은 알고리즘이며 알고리즘의 입력은 클래스입니다. 이름과 속성의 이름, 유형 및 값이 필요합니다. 여기에서 리플렉션이 발생합니다.이 정보에 액세스 할 수 있습니다. Java를 사용하면 Class
클래스 의 메소드를 사용하여 유형을 검사 할 수 있습니다 .
더 많은 사용 사례 :
그러나 완전한 반영은 기존 코드 ( "자체 검사"라고도 함)를 볼뿐만 아니라 코드를 수정하거나 생성하는 것을 의미합니다. 이를 위해 Java에는 두 가지 주요 사용 사례가 있습니다 : 프록시와 모의.
인터페이스가 있다고 가정 해 봅시다.
public interface Froobnicator {
void froobnicateFruits(List<Fruit> fruits);
void froobnicateFuel(Fuel fuel);
// lots of other things to froobnicate
}
흥미로운 것을 구현하는 구현이 있습니다.
public class PowerFroobnicator implements Froobnicator {
// awesome implementations
}
실제로 두 번째 구현도 있습니다.
public class EnergySaverFroobnicator implements Froobnicator {
// efficient implementations
}
이제 로그 출력도 원합니다. 메소드가 호출 될 때마다 단순히 로그 메시지가 필요합니다. 모든 메소드에 로그 출력을 명시 적으로 추가 할 수는 있지만 성가 시므로 두 번 수행해야합니다. 각 구현마다 한 번씩. (더 많은 구현을 추가 할 때 더 많은)
대신 프록시를 작성할 수 있습니다.
public class LoggingFroobnicator implements Froobnicator {
private Logger logger;
private Froobnicator inner;
// constructor that sets those two
public void froobnicateFruits(List<Fruit> fruits) {
logger.logDebug("froobnicateFruits called");
inner.froobnicateFruits(fruits);
}
public void froobnicateFuel(Fuel fuel) {
logger.logDebug("froobnicateFuel( called");
inner.froobnicateFuel(fuel);
}
// lots of other things to froobnicate
}
그러나 알고리즘에 의해 설명 될 수있는 반복적 인 패턴이 있습니다.
이 알고리즘의 입력은 인터페이스 정의입니다.
리플렉션을 사용하면이 알고리즘을 사용하여 새 클래스를 정의 할 수 있습니다. Java를 사용하면 java.lang.reflect.Proxy
클래스 의 메소드를 사용하여이를 수행 할 수 있으며 더 많은 기능을 제공하는 라이브러리가 있습니다.
반사의 단점은 무엇입니까?