2021 06 07
2021-06-07¶
리플렉션¶
- 정의한 Class
에 접근해보기 - 모든 클래스를 로딩한 후, Class의 인스턴스가 생김 public class Main { public static void main(String[] args) throws ClassNotFoundException { final Class<Test> classTypeTest = Test.class; System.out.println("classTypeTest = " + System.identityHashCode(classTypeTest)); final Test test = new Test(); final Class<? extends Test> getClassTest = test.getClass(); System.out.println("getClassTest = " + System.identityHashCode(getClassTest)); final Class<?> classForNameTest = Class.forName("Test"); System.out.println("classForNameTest = " + System.identityHashCode(classForNameTest)); } } >> 결과 classTypeTest = 460141958 getClassTest = 460141958 classForNameTest = 460141958- 클래스 로더에서 로딩이 완료된 클래스 같은 경우, Class 타입의 인스턴스가 힙 영역에 생기게 됨 - 해당 클래스를 위와 같은 방식으로 가져올 수 있고, 보다시피 주소가 같다!!! - 해당 클래스 인스턴스로 다음과 같은 일을 할 수 있음
//접근 지시자 public인 필드만 가져옴 Arrays.stream(classTypeTest.getFields()).forEach(System.out::println); //접근 지시자 상관없이 필드 다 가져옴 Arrays.stream(classTypeTest.getDeclaredFields()).forEach(System.out::println); //접근한 필드의 값을 가져와 보자 --> static이 아니라면 object가 필요하겠지? Arrays.stream(classForNameTest.getDeclaredFields()).forEach(f -> { try { f.setAccessible(true); System.out.print(f + " = "); System.out.println(f.get(test)); } catch (IllegalAccessException e) { e.printStackTrace(); } }); //메서드를 가져와 보자 여기서는 Object에서 상속받은 친구들까지 촤르르 나옴 Arrays.stream(getClassTest.getMethods()).forEach(System.out::println); //생성자를 가져와 보자 Arrays.stream(getClassTest.getConstructors()).forEach(System.out::println); //부모 클래스도 가져올 수 있음 System.out.println(getClassTest.getSuperclass()); //인터페이스도 가져올 수 있음 Arrays.stream(getClassTest.getInterfaces()).forEach(System.out::println); //접근 제어자에 대한 정보를 T/F 판별할 수 있음 Arrays.stream(getClassTest.getDeclaredFields()).forEach(f -> { final int modifiers = f.getModifiers(); System.out.println("f = " + f); System.out.println(Modifier.isPrivate(modifiers)); System.out.println(Modifier.isStatic(modifiers)); });
- 어노테이션
- 기본적으로 어노테이션은 주석! (약간의 의미와 기능이 추가된)
- Default로 "Class 레벨"로 어노테이션이 유지되기 때문에, 바이트 코드 컴파일 후에는 남아있지만, 런타임 환경들어가면 호로록 날라감
-
@Retention(RetentionPolicy.RUNTIME)해당 어노테이션을 통해 런타임까지 유지하게 설정 가능 - 중요 어노테이션 -@Retention: 해당 어노테이션을 언제까지 유지할 것인가? -@Inherit: 해당 어노테이션을 하위 클래스까지 전달할 것인가? -@Target: 어디에 사용할 수 있는지? - 리플렉션 - getAnnotations(): 상속받은 어노테이션까지 조회 - getDeclaredAnnotations(): 자기 자신에만 붙어있는 어노테이션 조회//어떤 어노테이션이 붙어있는가? Arrays.stream(getClassTest.getAnnotations()).forEach(System.out::println); //해당 어노테이션의 값은 무엇인가? Arrays.stream(getClassTest.getAnnotations()).forEach(f -> { if (f instanceof MyAnnotation) { MyAnnotation myAnnotation = (MyAnnotation) f; System.out.println("myAnnotation.name() = " + myAnnotation.name()); } });
- 리플렉션 API 활용
- 아래와 같이 클래스의 필드, 메서드에 해당하는 정보를 호출하고 사용할 수 있음//생성자를 리플렉션으로 가져와서 객체 생성하기 final Constructor<? extends Test> constructor = getClassTest.getConstructor(null); final Test test1 = constructor.newInstance(); System.out.println("test1 = " + test1); final Constructor<? extends Test> stringConstructor = getClassTest.getConstructor(String.class, String.class, String.class); final Test test2 = stringConstructor.newInstance("hi1", "hi2", "hi3"); System.out.println("test2 = " + test2); //Static 필드 가져와보기 (Static) final Field a = getClassTest.getDeclaredField("a"); a.setAccessible(true); System.out.println(a.get(null)); //오브젝트 안넘겨줘도 됨 a.set(null, "AAAAAAAAAAAAA"); //오브젝트 안넘겨줘도 됨 System.out.println(a.get(null)); //오브젝트 안넘겨줘도 됨 //인스턴스 필드 가져와보기 final Field c = getClassTest.getDeclaredField("c"); c.setAccessible(true); System.out.println(c.get(test2)); //오브젝트 안넘겨줘도 됨 c.set(test2, "CCCCCCCCCCCCCC "); //오브젝트 안넘겨줘도 됨 System.out.println(c.get(test2)); //오브젝트 안넘겨줘도 됨 //이번엔 메서드를 가져와보자 final Method f = getClassTest.getDeclaredMethod("f"); f.invoke(test1); final Method param = getClassTest.getDeclaredMethod("param", int.class, int.class); param.invoke(test1, 1, 3);