# Intro

## Java Reflection 소개

대부분의 프로그래밍에서는 내가 무슨 클래스의 객체를 만들어야 하는지 아는 것이 일반적이다. 하지만 이것을 모르는 상황이 있을 수 있다. 다음과 같은 상황을 가정해본다.

* `@Component`라는 Annotation이 설정된 클래스일 경우 객체를 생성
* 사용자의 선택에 따라 다른 클래스의 객체를 생성
* 코드를 변경할 수 없는 클래스의 구성요소가 private인 경우

위와 같은 상황에서는 Java Reflection이 좋은 대안이 될 수 있다.

### 데모 클래스&#x20;

예제에서 사용할 클래스는 다음과 같다.

{% code title="Student.java" %}

```java
package com.hacademy.reflection1;

public class Student {
	private String name;
	private int score;
	public Student() {
		super();
	}
	public Student(String name, int score) {
		super();
		this.name = name;
		this.score = score;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getScore() {
		return score;
	}
	public void setScore(int score) {
		this.score = score;
	}
	@Override
	public String toString() {
		return "Student [name=" + name + ", score=" + score + "]";
	}
}

```

{% endcode %}

### 예제 1 - 기본 객체 생성

Student 클래스의 객체를 new 연산자를 이용하여 생성하는 경우 코드는 다음과 같다.

{% code title="Test01.java" %}

```java
package com.hacademy.reflection1;

public class Test01 {
	public static void main(String[] args) {
		Student stu = new Student();
		stu.setName("피카츄");
		stu.setScore(100);
		System.out.println(stu.toString());
	}
}
```

{% endcode %}

new 연산자를 이용하여 객체를 생성하였고 setter 메소드를 이용하여 정보를 설정한 뒤 toString() 메소드를 통해 요약 정보를 확인하는 전형적인 객체 생성 코드이다.

### 예제 2 - Java Reflection을 사용하여 예제 1과 동일한      &#x20;

<mark style="color:blue;">Java Reflection</mark>을 이용하여 예제 1과 동일하게 객체를 생성하고 정보를 출력하기 위한 코드는 다음과 같다.

{% code title="Test02.java" %}

```java
package com.hacademy.reflection1;

import java.lang.reflect.InvocationTargetException;

public class Test02 {
	public static void main(String[] args) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
		Class<Student> clazz = Student.class;
		Student stu = clazz.getDeclaredConstructor().newInstance();
		stu.setName("피카츄");
		stu.setScore(100);
		System.out.println(stu.toString());
	}
}

```

{% endcode %}

실행하면 <mark style="color:red;">예제 1</mark>과 동일한 결과가 나온다. 하지만 코드는 많은 차이를 보인다. 우선 모든 클래스는 클래스 객체를 내장하고 있으며 class라는 이름의 변수로 접근 가능하다. 따라서 다음 코드는 Student의 클래스 정보를 clazz라는 변수에 저장하는 코드이다.

```java
Class<Student> clazz = Student.class;
```

Class 정보를 이용하여 객체를 생성하기 위해서는 생성자를 알아내야 한다. 우리는 Student라는 클래스의 코드를 알고 있고, 기본 생성자가 존재한다는 사실을 코드를 통해 확인할 수 있다. 따라서 다음 코드를 통해 기본 생성자 객체를 얻어낼 수 있다.

```java
clazz.getDeclaredConstructor()
```

얻어낸 생성자 객체를 이용하여 객체를 생성할 수 있다. 생성 시 다양한 예외가 발생하는데, 지정한 생성자가 없거나 권한이 없거나 등등의 여러 가지 오류와 관련된 위험이 존재하기 때문이다.&#x20;

```java
Student stu = clazz.getDeclaredConstructor().newInstance();
```

발생하는 예외는 try\~catch 처리를 하여도 되고 throws 처리를 하여도 된다. 상황에 맞게 처리하면 되고, 예제에서는 코드를 간결하게 만들기 위해 throws 처리를 하였다.

```java
public static void main(String[] args) 
                        throws InstantiationException, 
                                IllegalAccessException, 
                                IllegalArgumentException, 
                                InvocationTargetException, 
                                NoSuchMethodException, 
                                SecurityException {

}
```

### 예제 3 - 매개변수가 있는 생성자를 이용한 객체 생성

데모 클래스인 Student에는 모든 필드를 초기화하기 위한 생성자가 존재한다.

```java
public Student(String name, int score) {
    super();
    this.name = name;
    this.score = score;
}
```

이 생성자를 이용하여 객체를 생성하는 일반적인 코드는 다음과 같다.

{% code title="Test03.java" %}

```java
package com.hacademy.reflection1;

public class Test03 {
	public static void main(String[] args) {
		Student stu = new Student("피카츄", 100);
		System.out.println(stu.toString());
	}
}
```

{% endcode %}

### 예제 4 - Java Reflection을 사용하여 예제 3과 동일한 처리     &#x20;

{% code title="Test04.java" %}

```java
package com.hacademy.reflection1;

import java.lang.reflect.InvocationTargetException;

public class Test04 {
	public static void main(String[] args) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
		Class<Student> clazz = Student.class;
		Student stu = clazz.getDeclaredConstructor(String.class, int.class).newInstance("피카츄", 100);
		System.out.println(stu.toString());
	}
}
T
```

{% endcode %}

눈여겨볼 코드는 매개변수가 있는 생성자 객체를 반환하는 부분이다.

```java
clazz.getDeclaredConstructor(String.class, int.class)
```

getDeclaredConstructor()는 가변인자를 가지는 메소드이기 때문에 내부에 타입에 대한 Class 정보를 전달하여 생성자를 특정할 수 있다. 예제에서는 String과 int의 클래스 내장객체를 전달하여 매개변수가 (String, int)인 생성자 정보를 반환하도록 구현하였다.

그리고 `newInstance` 메소드를 통해 객체를 생성할 경우 생성자의 형태에 맞게 데이터를 인자로 전달하여 예제 3과 동일한 결과가 나오도록 구현하였다.

```java
Student stu = clazz.getDeclaredConstructor(String.class, int.class).newInstance("피카츄", 100);
```

이처럼 <mark style="color:blue;">Java Reflection</mark>을 이용하면 코드가 많이 달라지며, 예외도 다양하게 발생하기 때문에 위험성이 더 커진다.&#x20;

### 예제 5 - private 생성자인 경우

기존의 Student 클래스의 코드를 확인을 위해 두 개의 생성자를 모두 private으로 변경해본다.

{% code title="Student.java" %}

```java
package com.hacademy.reflection2;

public class Student {
	private String name;
	private int score;
	private Student() {
		super();
	}
	private Student(String name, int score) {
		super();
		this.name = name;
		this.score = score;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getScore() {
		return score;
	}
	public void setScore(int score) {
		this.score = score;
	}
	@Override
	public String toString() {
		return "Student [name=" + name + ", score=" + score + "]";
	}
}
```

{% endcode %}

<mark style="color:blue;">Java Reflection</mark>을 이용하면 생성자가 <mark style="color:red;">private</mark>이어도 객체 생성을 할 수 있다.

{% code title="Test01.java" %}

```java
package com.hacademy.reflection2;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class Test01 {
	public static void main(String[] args) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
		Class<Student> clazz = Student.class;
		Constructor<Student> constructor = clazz.getDeclaredConstructor();
		constructor.setAccessible(true);
		Student stu = constructor.newInstance();
		stu.setName("피카츄");
		stu.setScore(100);
		System.out.println(stu.toString());
	}
}

```

{% endcode %}

기존 예제와 달라진 점은 다음과 같다. 생성자가 private이기 때문에 접근을 허용하도록 설정하는 코드를 추가하였다.

```java
Constructor<Student> constructor = clazz.getDeclaredConstructor();
constructor.setAccessible(true);
```

접근을 허용한 뒤 동일한 방식으로 객체를 생성한다.

```java
Student stu = constructor.newInstance();
```

실행해보면 정상적으로 객체가 생성되는 것을 확인할 수 있으며, 이는 <mark style="color:red;">**자바의 기본 문법인 접근제한자의 규칙에 어긋나는 일**</mark>이다. 모든 필드를 초기화하는 생성자의 경우도 마찬가지이다.

{% code title="Test02.java" %}

```java
package com.hacademy.reflection2;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class Test02 {
	public static void main(String[] args) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
		Class<Student> clazz = Student.class;
		Constructor<Student> constructor = clazz.getDeclaredConstructor(String.class, int.class);
		constructor.setAccessible(true);
		Student stu = constructor.newInstance("피카츄", 100);
		System.out.println(stu.toString());
	}
}
```

{% endcode %}

이외에도 필드, 메소드 등에 대한 다양한 처리가 가능하며 하위 문서에서 살펴보도록 한다.   &#x20;
