2021.06.25 - [Java] - Reflection 에서 Reflection 이란 무엇인지, 그리고 왜 사용하는지, 어디에서 사용 되는지 등을 확인해 봤다.
이번 포스트에서는 실제로 Reflection을 직접 사용해 보고 어떻게 동작하는지 확인해 보자.
먼저 테스트를 위한 Class를 작성하자.
package com.example.demo.reflection;
public class A {
private String ps1 = "aaa";
private String ps2;
public String ps3;
private A() {
System.out.println("this is empty arg constructor");
}
private A(String ps2) {
this.ps2 = ps2;
}
public A(String ps1, String ps2) {
this.ps1 = ps1;
this.ps2 = ps2;
}
private void method1(String arg1, Integer arg2) {
System.out.println("this is mehtod1");
}
private void method2() {
System.out.println("this is mehtod2");
}
public void method3() {
System.out.println("this is method3");
}
}
String 2개를 받아 들이는 생성자 외에는 모두 private 으로 만들어서 외부에서 접근할 수 없도록 만들자.
A Class를 이용하여 Reflection이 어떻게 동작하는지 살펴보자.
Class
정의된 class를 가져오는 방법은 다음과 같다.
package com.example.demo.reflection;
import org.junit.Test;
public class ReflectionTest {
@Test
public void getClassTest() throws ClassNotFoundException {
Class clazz = A.class;
System.out.println("class name is : " + clazz.getName());
Class clazz2 = Class.forName("com.example.demo.reflection.A");
System.out.println("class2 name is : " + clazz2.getName());
}
}
java.lang.Class 형식으로 받기 위해서는 A.class 또는 Class.forName 을 사용할 수 있다.
forName 안에 인자로 들어가는 class 이름은 package 명까지 완벽하게 포함하여 입력해야 한다. 그렇지 않을 경우 ClassNotFoundException 을 발생 시키게 된다.
getClassTest 를 실행 시켜 보면 아래와 같은 결과가 나오게 된다.
class name is : com.example.demo.reflection.A
class2 name is : com.example.demo.reflection.A
Constructor
생성자를 가져오기 위해서는 아래와 같이 처리한다.
package com.example.demo.reflection;
import org.junit.Test;
import java.lang.reflect.Constructor;
public class ReflectionTest {
@Test
public void getConstructor() throws ClassNotFoundException, NoSuchMethodException {
Class clazz = Class.forName("com.example.demo.reflection.A");
Constructor constructor1 = clazz.getDeclaredConstructor();
System.out.println("constructor1 is : " + constructor1);
System.out.println("=============================");
Constructor constructor2 = clazz.getDeclaredConstructor(String.class);
System.out.println("constructor2 is : " + constructor2);
System.out.println("=============================");
Constructor constructors[] = clazz.getDeclaredConstructors();
for (Constructor constructor: constructors) {
System.out.println("constructors element is : " + constructor);
}
System.out.println("=============================");
Constructor constructors2[] = clazz.getConstructors();
for (Constructor constructor: constructors2) {
System.out.println("constructors element is : " + constructor);
}
}
}
getDeclaredConstructor()와 같이 인자를 넣지 않게 된다면 인자 없는 생성자를 가져온다.
getDeclaredConstructor(String.class)와 같이 String을 인자로 넣게 된다면 A Class의 생성자 중 String을 인자로 받는 생성자를 가져온다.
getDeclaredConstructors를 하게 되면 정의 된 생성자를 모두 가져 온다. foreach 문을 돌면서 확인해 보면 정의 된 생성자가 모두 보여지는 것을 알 수 있다.
getDeclaredConstructors 대신에 getConstructors를 사용하게 되면 public으로 정의 된 생성자만 반환하게 된다.
다음은 getConstructor 테스트 메소드를 실행한 결과이다.
constructor1 is : private com.example.demo.reflection.A()
=============================
constructor2 is : private com.example.demo.reflection.A(java.lang.String)
=============================
constructors element is : public com.example.demo.reflection.A(java.lang.String,java.lang.String)
constructors element is : private com.example.demo.reflection.A(java.lang.String)
constructors element is : private com.example.demo.reflection.A()
=============================
constructors element is : public com.example.demo.reflection.A(java.lang.String,java.lang.String)
Method
메소드는 다음과 같이 찾을 수 있다.
@Test
public void methodTest() throws ClassNotFoundException, NoSuchMethodException {
Class clazz = Class.forName("com.example.demo.reflection.A");
Method method1 = clazz.getDeclaredMethod("method1", null);
System.out.println("method is : " + method1);
}
A Class의 method1의 경우 파라미터가 없기 때문에 이름으로는 method1을 넘기고 파라미터로는 null을 넘기거나 아예 첫 번째 파라미터만 넘겨도 된다.
만일 method1을 String을 받는 형태로 바꾼 후 위의 테스트 코드를 실행하게 된다면 아래와 같은 NoSuchMethodException이 떨어지게 된다.
java.lang.NoSuchMethodException: com.example.demo.reflection.A.method1()
at java.lang.Class.getDeclaredMethod(Class.java:2130)
at com.example.demo.reflection.ReflectionTest.methodTest(ReflectionTest.java:44)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:220)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:53)
이럴 경우에는 다음과 같이 바꾸면 된다.
Method method1 = clazz.getDeclaredMethod("method1", String.class);
만일 2개 이상의 파라미터가 필요하다면 다음과 같이 하면 된다.
private void method1(String arg1, Integer arg2) {
System.out.println("this is mehtod1");
}
@Test
public void methodTest() throws ClassNotFoundException, NoSuchMethodException {
Class clazz = Class.forName("com.example.demo.reflection.A");
Class param[] = new Class[2];
param[0] = String.class;
param[1] = Integer.class;
Method method1 = clazz.getDeclaredMethod("method1", param);
System.out.println("method is : " + method1);
}
method1이 String arg와 Integer arg를 받게 되어 있으므로 Class 배열을 생성 한 다음 넘기면 된다.
전체 메소드를 보고자 할 경우에는 getDeclaredMethods를 사용하면 된다.
@Test
public void methodTest() throws ClassNotFoundException, NoSuchMethodException {
Class clazz = Class.forName("com.example.demo.reflection.A");
Class param[] = new Class[2];
param[0] = String.class;
param[1] = Integer.class;
Method method1 = clazz.getDeclaredMethod("method1", param);
System.out.println("method is : " + method1);
System.out.println("=============================");
Method methods[] = clazz.getDeclaredMethods();
for (Method method: methods) {
System.out.println("method is : " + method);
}
}
위와 같이 작성 한 후 실행 하게 되면 아래와 같이 출력되게 된다.
method is : private void com.example.demo.reflection.A.method1(java.lang.String,java.lang.Integer)
=============================
method is : private void com.example.demo.reflection.A.method1(java.lang.String,java.lang.Integer)
method is : private void com.example.demo.reflection.A.method2()
public method만 찾을 경우에는 getMethod를 사용하면 된다. A class에 public method를 다음과 같이 추가하고 테스트 해보면 알 수 있다.
public void method3() {
System.out.println("this is method3");
}
@Test
public void methodTest() throws ClassNotFoundException, NoSuchMethodException {
Class clazz = Class.forName("com.example.demo.reflection.A");
Method method3 = clazz.getMethod("method3");
System.out.println("method3 is : " + method3);
}
결과는 다음과 같다.
method3 is : public void com.example.demo.reflection.A.method3()
getMethods는 public method의 목록을 보여주며, 상속받은 모든 method들까지 보여 준다. 아래 내용을 통해 확인 할 수 있다.
@Test
public void methodTest() throws ClassNotFoundException, NoSuchMethodException {
Class clazz = Class.forName("com.example.demo.reflection.A");
Method methods2[] = clazz.getMethods();
for (Method method:methods2) {
System.out.println("method is : " + method);
}
}
method is : public void com.example.demo.reflection.A.method3()
method is : public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
method is : public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
method is : public final void java.lang.Object.wait() throws java.lang.InterruptedException
method is : public boolean java.lang.Object.equals(java.lang.Object)
method is : public java.lang.String java.lang.Object.toString()
method is : public native int java.lang.Object.hashCode()
method is : public final native java.lang.Class java.lang.Object.getClass()
method is : public final native void java.lang.Object.notify()
method is : public final native void java.lang.Object.notifyAll()
public method인 method3를 볼 수 있으며 그와 관련하여 상속받은 다른 메소드들까지 보여지게 된다.
Field
멤버 변수를 가져오기 위해서는 Field를 사용하면 된다. 테스트 코드는 아래와 같이 작성하였다.
@Test
public void getFieldsTest() throws ClassNotFoundException, NoSuchFieldException {
Class clazz = Class.forName("com.example.demo.reflection.A");
Field field1 = clazz.getDeclaredField("ps1");
System.out.println("field1 is : " + field1);
System.out.println("=============================");
Field fields1[] = clazz.getDeclaredFields();
for (Field field: fields1) {
System.out.println("field is : " + field);
}
System.out.println("=============================");
Field fields2[] = clazz.getFields();
for (Field field: fields2) {
System.out.println("field is : " + field);
}
}
getDeclaredField에 필드명을 넣으면 해당 이름을 가진 멤버 변수만을 가져오게 된다.
getDeclaredFields를 호출하게 되면 전체 멤버 변수를 가져 오게 된다.
getFields를 사용하면 public으로 생성 된 멤버 변수만 가져 오게 된다.
따라서 위의 테스트 코드를 실행하면 아래와 같이 나오게 된다.
field1 is : private java.lang.String com.example.demo.reflection.A.ps1
=============================
field is : private java.lang.String com.example.demo.reflection.A.ps1
field is : private java.lang.String com.example.demo.reflection.A.ps2
field is : public java.lang.String com.example.demo.reflection.A.ps3
=============================
field is : public java.lang.String com.example.demo.reflection.A.ps3
'Java' 카테고리의 다른 글
Public Interface의 품질에 영향을 미치는 요소... (0) | 2021.07.01 |
---|---|
Reflection 사용2 (0) | 2021.06.29 |
Reflection (0) | 2021.06.25 |
Java Random 함수의 동작 원리 (0) | 2021.05.14 |
모든 소프트웨어 개발자가 알아야 할 디자인 패턴2 (0) | 2021.03.15 |