forked from depromeet/effective-java-study
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[item65]: 리플렉션보다는 인터페이스를 사용하라라 (depromeet#146)(건호)
- Loading branch information
1 parent
529d797
commit bc5a07f
Showing
1 changed file
with
106 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
# Item 65. 리플렉션보다는 인터페이스를 사용하라 | ||
|
||
### Reflection | ||
|
||
오라클 API 문: | ||
|
||
Reflection is commonly used by programs which require the ability to examine or modify the runtime behavior of applications running in the Java virtual machine. This is a relatively advanced feature and should be used only by developers who have a strong grasp of the fundamentals of the language. With that caveat in mind, reflection is a powerful technique and can enable applications to perform operations which would otherwise be impossible. | ||
|
||
- 리플랙션은 Java 가상 머신에서 실행되는 응용 프로그램의 런타임 동작을 조사하거나 수정해야 하는 프로그램에서 주로 사용된다. | ||
- 이 기능은 상대적으로 고급 기능이므로 언어의 기본 원칙을 잘 이해하고 있는 개발자만 사용해야 한다. | ||
- 이 주의사항을 염두에 두면, 리플랙션은 강력한 기술이다. 그리고 그렇지 않으면 불가능한 작업을 수행할 수 있게 한다. | ||
|
||
<br/> | ||
|
||
### 올바른 사용 예시 | ||
|
||
**프레임워크나 라이브러리 제작**: | ||
대표적으로 Spring과 같은 프레임워크에서는 리플랙션을 사용하여 객체를 생성하거나 메서드를 호출한다. 사용자가 정의한 클래스와 메서드에 대한 메타 데이터를 얻기 위해 사용한다. | ||
|
||
```java | ||
public Object createInstance(String className) throws Exception { | ||
Class<?> clazz = Class.forName(className); | ||
return clazz.newInstance(); | ||
} | ||
``` | ||
|
||
**제네릭 타입 검사**: | ||
제네릭 정보는 런타임에 지워지기 때문에(타입 소거) 리플랙션을 사용하여 해당 정보를 얻을 수 있다. | ||
|
||
```java | ||
public <T> void genericTypeCheck(T obj) { | ||
Class<?> clazz = obj.getClass(); | ||
System.out.println("제네릭 타입은: " + clazz.getSimpleName()); | ||
} | ||
``` | ||
|
||
<br/> | ||
|
||
### 잘못 사용한 예시 | ||
|
||
**비공개 필드나 메서드 접근**: | ||
비공개 필드나 메서드에 접근하려고 리플랙션을 사용하는 것은 권장되지 않는다. 그 이유는 개발자가 비공개로 설정한 것은 내부 동작을 위해 숨겨진 것이기 때문이다. | ||
|
||
```java | ||
import java.lang.reflect.Field; | ||
|
||
public class BadReflectionExample { | ||
private String secret = "비밀 정보"; | ||
|
||
public static void main(String[] args) throws Exception { | ||
BadReflectionExample example = new BadReflectionExample(); | ||
Field field = BadReflectionExample.class.getDeclaredField("secret"); | ||
field.setAccessible(true); | ||
System.out.println("비공개 필드 접근: " + field.get(example)); | ||
} | ||
} | ||
``` | ||
|
||
**비효율적인 객체 생성**: | ||
Class.newInstance() 를 사용하여 객체를 생성하는 것은 해당 클래스의 기본 생성자가 호출되지만, 생성자에서 발생하는 예외는 래핑되어 다시 던져진다. 따라서 Constructor.newInstance()를 사용하는 것이 더 안전하다. | ||
|
||
```java | ||
// 잘못된 방식 | ||
Object obj = SomeClass.class.newInstance(); | ||
|
||
// 올바른 방식 | ||
Constructor<SomeClass> constructor = SomeClass.class.getDeclaredConstructor(); | ||
Object obj = constructor.newInstance(); | ||
``` | ||
|
||
**타입 안전성 무시**: | ||
리플랙션을 사용하면 컴파일 타임에 확인할 수 있는 타입 안전성을 무시하게 되어 런타임 에러의 위험이 증가한다. | ||
|
||
```java | ||
public void unsafeSet(Object obj, Object value) throws Exception { | ||
Field field = obj.getClass().getDeclaredField("someField"); | ||
field.setAccessible(true); | ||
field.set(obj, value); // 타입 검사가 없다면, 잘못된 타입의 값이 설정될 수 있다. | ||
} | ||
``` | ||
|
||
<br/> | ||
|
||
### 리플랙션의 단점 | ||
|
||
- `성능 이슈`: 리플랙션은 런타임에 타입을 검사하고 조작하기 때문에 직접 코드를 호출하는 것에 비해 성능 손실이 발생한다. | ||
|
||
- `보안 제한`: 보안 매니저가 설정되어 있는 경우, 리플랙션을 사용하여 접근하려는 클래스나 메서드에 대한 권한이 없을 수 있다. | ||
|
||
- `내부 API 및 변경 가능성`: 리플랙션을 사용하여 접근하는 클래스나 메서드가 미래의 Java 버전에서 변경되거나 사라질 수 있다. 내부 API에 대한 접근은 항상 위험하다. | ||
|
||
- `복잡성 증가`: 리플랙션 코드는 복잡하며 디버깅하기 어렵다. 코드의 가독성도 저하된다. | ||
|
||
결론적으로, 리플랙션은 필요할 때만 사용하고, 그 사용을 최소화해야 한다. 가능하면 컴파일 타임에 알려진 타입과 API를 사용하는 것이 바람직하다. | ||
|
||
<br/> | ||
|
||
### 정리 | ||
|
||
- 리플렉션은 Java에서 제공하는 매우 강력한 기능이다. 이를 통해 런타임에 객체의 내부 정보를 조사하거나 조작할 수 있다. 특히 프레임워크나 라이브러리의 개발, 플러그인 시스템 구현 등 복잡하고 특수한 시스템을 개발할 때 유용하다. | ||
|
||
- 그러나 이 강력한 기능은 단점도 많다. 성능 이슈, 보안 제한, 코드 복잡성 증가 등이 대표적인 문제점이다. 따라서 리플렉션은 반드시 필요한 경우에만 사용하고, 그 범위를 최소화하는 것이 바람직하다. | ||
|
||
- 특히 객체를 생성할 때는 리플렉션을 사용할 수 있지만, 생성된 객체를 다룰 때는 가능한 적절한 인터페이스나 알려진 상위 클래스로 형변환하여 사용해야 한다. 이렇게 하면 코드의 안정성과 가독성, 유지보수성을 높일 수 있다. | ||
|
||
결론적으로, 리플렉션은 `필요한 경우에만, 그리고 신중하게` 사용해야 하는 기술이다. 가능하면 컴파일 타임에 타입이 알려진, 안전한 방법을 우선적으로 고려해야 한다. |