반사 와 범 형

전송:
http://www.gd-emb.org/detail/id-33870.html
요약:반사 와 범 형의 기능 이 매우 강하 고 이들 을 결합 시 키 는 것 도 매우 재 미 있 습 니 다.본 고 는 자바 언어의 반사 와 범 형의 관계 에 대해 연 구 를 하고 벽돌 을 던 져 옥 을 끌 어 들 여 두 가지 기술 에 대한 흥 미 를 끌 려 고 합 니 다!
범 형 과 반사 사이 의 관 계 를 연구 하 는 것 은 매우 재미있다.
우 리 는 반사 와 범 형 이 모두 자바 의 동적 기술 이라는 것 을 안다.계승 과 다 형 이 아 닌 대상 을 향 한 기술 이다.이렇게 말 하면 반사 와 범 형 은 모두 계승 과 다 형 등 대상 을 대상 으로 하 는 기술 의 부족 을 보완 하기 위해 생 긴 것 같다.모델 은 주로 대상 을 대상 으로 하 는 기술 을 바탕 으로 하 는 것 이기 때문에 모든 모델 은 동태 성 이 많 거나 확장 성 이 부족 하 다 고 할 수 있다.우 리 는 반사 와 범 형 을 결합 하여 모델 에 대해 일정한 확장 을 하여 동태 적 인 측면 에서 우리 의 요구 에 더욱 부합 하도록 한다.
이런 기술 을 결합 시 키 는 과정 에서 우 리 는 범 형 과 반 사 를 결합 시 킬 생각 을 거의 하지 못 할 것 이다.이것 은 매우 재 미 있 는 화제 이다.범례 와 반 사 는 자바 로 하여 금 일정한 확장 성 을 가지 게 하지만 동시에 자신의 부족 도 있다.이 두 가지 기술 을 결합 하면 각자 의 부족 을 해결 하고 동태 적 으로 한 단계 더 향상 시 킬 수 있 지 않 을 까?
앞에서 말 한 것 처럼 범 형 과 반 사 는 서로 촉진 할 수 있 으 니 우 리 는 먼저 범 형 이 어떻게 반 사 를 돕 는 지 살 펴 보 자.
우 리 는 반 사 를 사용 하 는 가장 큰 고민 중 하 나 는 반 사 를 응용 한 후에 얻 는 것 이 기본적으로 Object 유형의 대상 이라는 것 을 알 고 있다.이런 대상 을 사용 하려 면 우리 가 강제 유형 전환 을 해 야 한다.그리고 우 리 는 범 형의 기능 중 하 나 는 우리 가 사용 하 는 강제 유형 전환 을 없 애 는 것 이라는 것 을 더욱 잘 알 고 있다.
1.실행 기간 내 대상 초기 화
운행 기간 내 초기 화 대상 은 우리 가 가장 자주 사용 하 는 반사 기능 이지 만,우리 가 반 사 를 통 해 운행 기간 내 에 얻 은 대상 의 유형 은 보통 Object 유형 이 고,이 대상 은 우리 가 사용 할 때 강제 유형 전환 을 해 야 한다.지금 은 반사 가 있어 서 우 리 는 강제 유형 전환 이라는 일 을 할 필요 가 없다.
만약 에 우리 가 이미 두 가지 유형 이 있다 고 가정 하면:

  public class Cls1 {
    public void do1() {
      // TODO Auto-generated method stub
      System.out.println("cls1...");
    }
  }
  public class Cls2 {
    public void do2() {
      // TODO Auto-generated method stub
      System.out.println("cls2...");
    }
  }

우 리 는 운행 기간 내 에 이 두 대상 을 초기 화 해 야 합 니 다.우 리 는 다음 과 같은 초기 화 방법 을 설계 할 수 있 습 니 다.

public class Factory{
public static <U extends Object>U getInstance(String clsName)
{
try
{
Class<?> cls = Class.forName(clsName);
return (U) cls.newInstance();
}
catch(Exception e)
{
e.printStackTrace();
return null;
}
}
} 

이 방법 에서 우 리 는 범 형 을 이용 하여 초기 화 방법 에서 강제 유형 전환 작업 을 미리 했 기 때문에 우 리 는 사용 할 때 강제 유형 전환 작업 을 하지 않 아 도 된다.
그들의 테스트 코드 는 다음 과 같다.

Cls1 i1 = Factory.getInstance("fanxing.factory.dynaFactory.Cls1");
i1.do1();
Cls2 i2 = Factory.getInstance("fanxing.factory.dynaFactory.Cls2");
i2.do2() 

테스트 결 과 는:
cls1...
cls2...
주의해 야 할 것 은 이런 방법 을 사용 하 는 데 몇 가지 문제 가 있다 는 것 이다.
첫째,return(U)cls.newInstance();이 문 구 는 경고 적 인 오류 가 있 을 것 이다.다행히 이런 오 류 는 컴 파일 적 인 오류 가 아니 라 우리 가 감당 할 수 있 는 것 이다.
둘째,컴 파 일 러 는 유형 검 사 를 할 수 없습니다.예 를 들 어 Cls 1 i1=Factory.getInstance("fanxing.factory.dynaFactory.Cls 1");이 문 구 는 Cls 2 i1=Factory.getInstance("fanxing.factory.dynaFactory.Cls 1")로 바 꿉 니 다.번역 할 수 있 습 니 다.운행 중 에 만 오류 가 발생 합 니 다.
위의 방법 외 에 더 좋 은 방법 이 있다.
이 방법 은 우리 가 운행 기간 내 에 들 어 오 는 대상 이 Class 대상 이 어야 합 니 다.물론 일반적인 Class 대상 을 거 쳐 아래 의 모습 과 같 습 니 다.

Class<Cls1> cls = Cls1.class;
try
{
Intf1 obj = cls.newInstance();
obj.do1();
}
catch(Exception e)
{
e.printStackTrace();
} 

여기 에는 강제 유형 전환 이 전혀 없고 물론 첫 번 째 방법 으로 나타 난 두 가지 문제 도 없다 는 것 을 잘 알 수 있다.
실행 결 과 는:
cls1...
이 방법 에 따라 우 리 는 앞의 초기 화 방법 을 다음 과 같이 개조 할 수 있다.

public static <U extends Object> U getInstance(Class<U> cls)
{
try
{
return cls.newInstance();
}
catch(Exception e)
{
e.printStackTrace();
return null;
}
} 

다음 테스트 를 진행 하 겠 습 니 다.
Cls1 c1 = Factory.getInstance(Cls1.class);
c1.do1();
테스트 결 과 는:
cls1...
이때,만약 우리 가 위의 테스트 코드 를 다음 으로 바꾼다 면:
Cls2 c1 = Factory.getInstance(Cls1.class);
컴 파일 오류 가 발생 할 수 있 습 니 다.우리 의 두 번 째 방법 은 첫 번 째 방법의 약점 을 피 할 수 있 습 니 다.그러나 첫 번 째 방법 은 초기 화 방법 에 클래스 이름 을 매개 변수 로 입력 하 는 것 이 좋 습 니 다.
2.운행 기간 내 호출 방법
반 사 를 사용 한 사람 은 운행 기간 내 호출 방법 을 사용 한 결과 도 Object 대상 이 될 것 이라는 것 을 잘 알 고 있다.이런 결 과 는 일반적인 방법 반사 에서 견 딜 수 있다.그러나 때때로 참 을 수 없다.예 를 들 어 우 리 는 List류 의 iterator()방법 을 반사 하고 싶다.
일반적으로 우 리 는 반 사 를 사용 하여 이렇게 할 수 있다.
try
{
Class<?> cls = Class.forName("java.util.ArrayList");
Method m = cls.getDeclaredMethod("iterator",new Class[0]);
return m.invoke(this,new Object[0]);
}
catch(Exception e)
{
e.printStackTrace();
return null;
} 

물론,여기 서 되 돌아 오 는 것 은 Object 대상 이 고,List류 의 iterator()의 실제 되 돌아 오 는 유형 은 T 입 니 다.분명히 우 리 는 위의 코드 를 다음 과 같이 수정 할 수 있다.

try
{
Class<?> cls = Class.forName("java.util.ArrayList");
Method m = cls.getDeclaredMethod("iterator",new Class[0]);
return (T)m.invoke(this,new Object[0]);
}
catch(Exception e)
{
e.printStackTrace();
return null;
}

마찬가지 로 우리 코드 도 경고 적 인 오 류 를 만 날 수 있다.하지만 우 리 는 내 버 려 둘 수 있다.
3.실행 기간 동안 배열 대상 초기 화
마찬가지 로 실행 기간 내 에 초기 화 된 배열 도 Object 대상 입 니 다.아래 모습 처럼:
Object o = Array.newInstance(int.class,10);
getArray()방법 에서 배열 대상 을 얻 고 싶다 면 아래 와 같은 코드 를 얻 을 수 있 습 니 다.
public Object getArray(Class cls,int size)
{
return Array.newInstance(cls,size);
}

이것 은 분명히 우 리 를 만족 시 키 지 못 할 것 이다.클래스형식의 인 자 를 입력 하면 T 형식의 결 과 를 되 돌려 주 고 싶 기 때 문 입 니 다.
이러한 생각 에서 우 리 는 getArray()방법 을 다음 과 같이 수정 합 니 다.
public T[] getArray(Class<T> cls,int size)
{
return (T[])Array.newInstance(cls,size);
}

이런 수정 은 우리 로 하여 금 비교적 만 족 스 러 운 결 과 를 얻 게 할 것 이다.마찬가지 로 위의 코드 도 경고 적 인 오 류 를 얻 을 수 있다.그러나 범 형 을 사용 하여 우리 의 결 과 를 크게 최적화 시 켰 다.
위의 몇 가지 예 는 우리 에 게 범 형 이 반 사 를 도 울 수 있 고 반사 결 과 를 크게 최적화 시 켰 다 는 것 을 보 여 주 었 다.그러나 반사 도 범 형의 도움 을 수 동적 으로 받 아들 이 는 것 이 아니 라 반사 도 범 형 을 도 울 수 있다.이것 은 반 사 를 바탕 으로 하 는 기본 적 인 작업 원리 이다.데 이 터 를 얻 은 메타 데이터 이다.반 사 를 통 해 범 형의 메타 데 이 터 를 얻 을 수 있다 는 것 이다.이 밖 에 반사 도 범 형 데이터 운행 기간 내 초기 화 에 도움 이 된다.
다음은 한두 가지 간단 한 예 로 설명 한다.
4.반사 로 범 형 류 초기 화
우 리 는 보통 한 종류 에서 이렇게 범 형 을 사용한다.

public final class Pair<A,B> {
public final A fst;
public final B snd;

public Pair(A fst, B snd) {
this.fst = fst;
this.snd = snd;
}
……
}

이것 은 당연히 우리 의 가장 기본 적 인 용법 이지 만 자주 이렇다.컴 파 일 러 는 이 미 지 의 대상,예 를 들 어 A fst 와 같은 정 보 를 더 알 고 싶다.그러면 우 리 는 운행 기간 내 에 어떤 방법 을 호출 할 수 있다.여러분,이것 은 매우 쉽 습 니 다.우 리 는 이런 미 지 의 유형 을 매개 변수 로 입력 할 수 있 습 니 다.하하,이것 이 맞다.이런 매개 변수 가 있 으 면 다음 단계 에 우 리 는 운행 기간 내 에 반사 되 어 그것 을 호출 하 는 방법 을 사용 해 야 한다.
이런 예 에 대하 여 나 는 여기에서 더 이상 제시 하지 않 겠 다.나 는 여기 서 간단 한 예 를 하나 제시 하 는데,바로 범 형 류 초기 화 에 필요 한 구조 기 를 사용 하 는 것 이다.
위의 Pair류 에 대해 구조 기의 입력 매개 변수 유형 이 A 와 B 가 아니 라 Class와 Class라면 구조 기 에서 반 사 를 사용 해 야 합 니 다. public Pair(Class<A> typeA, Class<B> typeB) { this.fst = typeA.newInstance(); this.snd = typeB.newInstance(); …… } 이 를 통 해 알 수 있 듯 이 범 형 에 있 는 알 수 없 는 유형의 매개 변수 에 대해 우 리 는 일반 유형 과 마찬가지 로 반사 도 구 를 사용 할 수 있다.즉,이러한 알 수 없 는 유형의 매개 변 수 를 반사 하여 할 수 있 는 모든 일 을 할 수 있다.5.반사 로 일반적인 정 보 를 얻는다.이 작은 매듭 에 관 한 문 제 는 더욱 재미 있어 보인다.우 리 는 위의 Pair를 예 로 들 었 습 니 다.만약 에 우리 가 한 종류 에서 이런 종 류 를 사용 했다 면 다음 과 같 습 니 다. public Class PairUser { private Pair<String,List> pair; …… } 만약 에 우리 가 PairUser 류 응용 에 반 사 를 한다 면 우 리 는 이러한 속성 pair 의 일부 정 보 를 쉽게 얻 을 수 있 습 니 다.예 를 들 어 private 인지 Public 인지,그 유형 등 입 니 다.만약 에 우리 가 반 사 를 통 해 pair 속성의 유형 이 Pair 라 는 것 을 얻 은 후에 우 리 는 이 유형 이 범 형 류 라 는 것 을 알 게 된다 면 우 리 는 이 범 형 류 의 더욱 진일보 한 정 보 를 알 고 싶 습 니 다.예 를 들 어 범 형 류 의 유형 명,범 형 매개 변수 에 대한 정보 등 이다.구체 적 으로 위의 PairUser 류 의 예 를 들 어 저 는 현재 그의 속성 pair 의 정 보 를 알 고 싶 습 니 다.예 를 들 어 그의 유형 명,일반적인 매개 변수 유형,예 를 들 어 String 과 List 등 정 보 를 알 고 싶 습 니 다.해 야 할 일 은 다음 과 같다.우선 이 속성 을 획득 합 니 다.Field field = PairUser.class.getDeclaredField("pair"); 그리고 속성 을 얻 는 일반적인 유형 입 니 다.Type gType = field.getGenericType(); gType 이 Parameterized Type 형식 인지 여 부 를 판단 하고,만약 그렇다면 Parameterized Type 형식의 변수 로 전환 합 니 다.ParameterizedType pType = (ParameterizedType)gType; 원본 형식 가 져 오기Type rType = pType.getRawType(); 그리고 rType.getClass().getName()을 통 해 속성 pair 의 유형 명 을 얻 을 수 있 습 니 다.마지막 으로 매개 변수 정보 가 져 오기Type[] tArgs = pType.getActualTypeArguments(); tArgs[j].getClass().getName()을 통 해 속성 pair 의 일반적인 매개 변수의 유형 명 을 얻 을 수 있 습 니 다.전체 코드 는 다음 과 같 습 니 다: try { Field field = PairUser.class.getDeclaredField("pair"); Type gType = field.getGenericType(); if(gType instanceof ParameterizedType) { ParameterizedType pType = (ParameterizedType)gType; Type rType = pType.getRawType(); System.out.println("rawType is instance of " + rType.getClass().getName()); System.out.println(" (" + rType + ")"); Type[] tArgs = pType.getActualTypeArguments(); System.out.println("actual type arguments are:"); for (int j = 0; j < tArgs.length; j++) { System.out.println(" instance of " + tArgs[j].getClass().getName() + ":"); System.out.println(" (" + tArgs[j] + ")"); } } else { System.out.println("getGenericType is not a ParameterizedType!"); } } catch(Exception e) { e.printStackTrace(); } } 출력 결 과 는:rawType is instance of java.lang.Class (class Pair) actual type arguments are: instance of java.lang.Class: (class java.lang.String) instance of java.lang.Class: (interface java.util.List)

좋은 웹페이지 즐겨찾기