Closed ghojeong closed 3 years ago
리플렉션 성능에 관한 글입니다. 가볍게 볼만해요 https://yjacket.tistory.com/73
한번 언급했던 것 같은데, Spring에서는 사용되는 어노테이션들이 이 리플렉션을 기반으로 동작하는 걸로 알고 있습니다. 그에 대한 흐름을 아래 포스팅에서 조금 파악할 수 있을듯 합니다! https://sas-study.tistory.com/271
p374.. 이 프로그램을 컴파일 하면... 비검사 형변환 경고가 뜬다. 하지만 Class<? extends Set
> 으로 형변환은 심지어 명시한 클래스가 Set을 구현하지 않았더라도 성공할 것이다.
현재 백기선님 더 자바 강의를 보고있는데 https://sas-study.tistory.com/271 링크와 똑같습니다. 호눅스가 추천하셨던 백기선님 강의 더 자바, 코드를 조작하는 다양한 방법 좋으니 만약 여유가 있으시면 꼭 보는걸 추천합니다.
@Entity
클래스에 Setter가 없을 시 리플렉션을 사용한다.한번 언급했던 것 같은데, Spring에서는 사용되는 어노테이션들이 이 리플렉션을 기반으로 동작하는 걸로 알고 있습니다. 그에 대한 흐름을 아래 포스팅에서 조금 파악할 수 있을듯 합니다! https://sas-study.tistory.com/271
와 진짜 좋은 글이네요. 한번 리플렉션을 이번주에 간단하게나마 예시로 공부해봐야겠다는 생각이드네요.
저번에 David 가 리플렉션을 이용해서 Annotation 이 동작한다고 들었던것 같은데, 이번 기회에 공부해봐도 좋겠네요.
리플렉션을 이용해서 Annotation 정보를 가져오거나 조작할 수 있다고 생각할 수 있군요
p374.. 이 프로그램을 컴파일 하면... 비검사 형변환 경고가 뜬다. 하지만 Class<? extends Set> 으로 형변환은 심지어 명시한 클래스가 Set을 구현하지 않았더라도 성공할 것이다.
- 왜 성공하는 걸까요?
- 왜 인스턴스를 생성하려 할 때 오류가 발생할까요?
질문 뒤늦게 확인해서 답변 올립니다.
Class<? extends Set<String>> cl = (Class<? extends Set<String>>) Class.forName("java.lang.String"); // 1번
Constructor<? extends Set<String>> cons = cl.getDeclaredConstructor(); // 2번
Set<String> s = cons.newInstance(); // 3번
위 코드의 런타임에서 왜 1번과 2번은 성공하고 3번은 실패하는 걸까요?
저는 인터페이스가 구현하기를 요구하는 메서드가 인스턴스 내에 존재하느냐의 유무로 달라진다고 이해했습니다.
가령 Class.forName("java.lang.String")
은 비록 Class<? extends String>
타입을 반환할 테지만,
명시적으로 형변환을 하고 있고, 그에 대해 Class
클래스가 가져야 하는 필드와 메서드를 모두 가지고 있으므로,
런타임에서의 실행에서도 에러를 일으키지 않습니다.
성공을 왜 하느냐에 대한 이해는 제너릭을 전부 지우니 더 직관적으로 이해가 가더군요.
Class cl = Class.forName("java.lang.String"); // 1번
Constructor cons = cl.getDeclaredConstructor(); // 2번
위 코드는 정상작동합니다. 제너릭이 없어서 허전한것 말고는, 코드 상으로도 이해해보려 했을 떄 전혀 문제될게 없어보입니다. 기존의 1번과 2번 코드는 제너릭을 명시하고, 명시적인 형변환 코드를 추가했을 뿐입니다. 즉 컴파일 타임에서의 명시를 강화했을 뿐이지, 로직상으로 달라진게 하나도 없습니다. 그리고 왜 정상작동하는지에 대해서는 너무나 직관적이고 명확합니다. 가능한 타입에, 가능한 인스턴스를 할당했을 뿐입니다.
다만, 3번의 실패는 이야기가 다릅니다.
1번과 2번은 제너릭이 다를 뿐 인터페이스는 동일합니다.(Class<? extends Set
java.lang.ClassCastException: class java.lang.String cannot be cast to class java.util.Set (java.lang.String and java.util.Set are in module java.base of loader 'bootstrap')
즉 성공과 실패여부의 차이는 아래와 같다고 이해했습니다.
Class cl = Class.forName("java.util.HashSet"); // 1번
Constructor<? extends Set> cons = cl.getDeclaredConstructor(); // 2번
Set s = cons.newInstance(); // 3번
위 코드는 정상 작동합니다.
단 Constructor<? extends Set>
에서 <? extends Set>
제너릭을 제거하면,
cons.newInstance()
를 했을 떄 컴파일 타임 중 캐스팅이 실패하므로 명시를 해주어야 합니다.
// 실패
Class cl = Class.forName("java.util.HashSet");
Constructor cons = cl.getDeclaredConstructor();
Set s = cons.newInstance();
// 성공
Class cl = Class.forName("java.util.HashSet");
Constructor cons = cl.getDeclaredConstructor();
Set s = (Set) cons.newInstance();
즉 컴파일 타임 중의 타입 캐스팅은 명시만 한다면, generic 이 없거나 이상하더라도, 타입 캐스팅으로 인한 에러는 발생하지 않는 것을 알 수 있습니다.
https://docs.oracle.com/javase/9/docs/api/java/lang/Class.html
newInstance()
는 deprecated 되었으니 생성자를 통해서 만드는것을 추천합니다.
Class 에도 newInstance 있다는거 처음 알았는데 감사합니다!
리플렉션의 자바 문서:
Class Class <T>
https://docs.oracle.com/javase/8/docs/api/java/lang/Class.html