자바 기본 API는 여러 절로 구성되어 있습니다.
StringTokenizer, StringBuffer, StringBuilder Class
Regular Expression & Pattern Class
Class Class
자바는 클래스와 인터페이스의 메타 데이터를 java.lang 패키지에 소속된 Class 클래스로 관리합니다. 여기서 메타 데이터는 클래스의 이름, 생성자 정보, 필드 정보, 메소드 정보를 말합니다.
Class 객체 얻기(getClass(), forName())
프로그램에서 Class 객체를 얻기 위해서는 Object 클래스가 가지고 있는 getClass() 메소드를 이용하면 됩니다. Object는 모든 클래스의 최상위 클래스이므로 모든 클래스에서 getClass() 메소드를 호출할 수 있습니다.
1 2 | Class clasis = obj.getClass(); | cs |
getClass() 메소드는 해당 클래스로 객체를 생성했을 때만 사용할 수 있는데, 객체를 생성하기 전에 직접 Class 객체를 얻을 수도 있습니다. Class는 생성자를 감추고 있기 때문에 new 연산자로 객체를 만들 수 없고, 정적 메소드인 forName()을 이용해야 합니다. forName() 메소드는 클래스 전체 이름(패키지가 포함된 이름)을 파라미터로 받고 Class 객체를 리턴합니다.
1 2 3 4 5 | try { Class clasis = Class.forName(String className); } catch( ClassNotFoundException e ) { } | cs |
Class.forName() 메소드는 파라미터로 주어진 클래스를 찾지 못하면 ClassNotFoundException 예외를 발생시키기 때문에 예외 처리가 필요합니다. 다음은 두 가지 방법으로 Car 클래스의 Class 객체를 얻고, Class의 메소드를 이용해 클래스의 전체 이름과 간단한 이름 그리고 패키지 이름을 얻어 출력합니다.
* ClassExam.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | package api; public class ClassExam { public static void main(String[] args) { Car car = new Car(); Class<? extends Car> clasis1 = car.getClass(); System.out.println(clasis1.getName()); System.out.println(clasis1.getSimpleName()); System.out.println(clasis1.getPackage().getName()); System.out.println(); try { Class<?> clasis2 = Class.forName("api.Car"); System.out.println(clasis2.getName()); System.out.println(clasis2.getSimpleName()); System.out.println(clasis2.getPackage().getName()); } catch(ClassNotFoundException e) {} } } class Car { } | cs |
리플렉션
Class 객체를 이용하면 클래스의 생성자, 필드, 메소드 정보를 알아낼 수 있습니다. 이것을 리플렉션(Reflection)이라고 합니다. Class 객체는 리플렉션을 위해 getDeclaredConstructors(), getDeclaredFields(), getDeclaredMethods() 를 제공하고 있습니다.
1 2 3 4 | Constructor[] constructors = clasis.getDeclaredConstructors(); Field[] fields = clasis.getDeclaredFields(); Method[] methods = clasis.getDeclaredMethods(); | cs |
Constructor, Field, Method 클래스는 모두 java.lang.reflect 패키지에 속해 있습니다. getDeclaredFields()와 getDeclaredMethods()는 클래스에 선언된 멤버만 가져오고 상속된 멤버는 가져오지 않습니다.
다음은 Car 클래스에서 선언된 생성자, 필드, 메소드의 정보를 얻고 출력합니다.
* ReflectExam.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | package api; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; public class ReflectExam { public static void main(String[] args) throws Exception { Class<?> clasis = Class.forName("api.Car"); System.out.println("[클래스 이름]"); System.out.println(clasis.getName()); System.out.println(); System.out.println("[생성자 정보]"); Constructor<?>[] constructors = clasis.getDeclaredConstructors(); for (Constructor<?> constructor : constructors) { System.out.print(constructor.getName() + "("); Class<?>[] parameters = constructor.getParameterTypes(); printParameters(parameters); System.out.println(")"); } System.out.println(); System.out.println("[필드 정보]"); Field[] fields = clasis.getDeclaredFields(); for (Field field : fields) { System.out.println(field.getType().getSimpleName() + " " + field.getName()); } System.out.println(); System.out.println("[메소드 정보]"); Method[] methods = clasis.getDeclaredMethods(); for (Method method : methods) { System.out.print(method.getName() + "("); Class<?>[] parameters = method.getParameterTypes(); printParameters(parameters); System.out.println(")"); } } private static void printParameters(Class<?>[] parameters) { for (int i = 0; i < parameters.length; i++) { System.out.print(parameters[i].getName()); if (i < (parameters.length - 1)) { System.out.print(", "); } } } } | cs |
실행 결과는 Car 클래스가 자체적으로 가지고 있는 public 멤버와 상위 클래스인 Object가 가지고 있는 public 멤버들이 모두 출력되고, private 멤버들은 출력되지 않는 것을 볼 수 있습니다.
동적 객체 생성(newInstance())
Class 객체를 이용하면 new 연산자를 사용하지 않고도 동적으로 객체를 생성할 수 있습니다. 이 방법은 코드 작성 시에 클래스 이름을 결정할 수 없고, 런타임 시에 클래스 이름이 결정되는 경우에 매우 유용하게 사용될 수 있습니다.
다음 코드처럼 Class.forName() 메소드로 Class 객체를 얻은 다음 newInstance() 메소드를 호출하면 Object 타임의 객체를 얻을 수 있습니다.
1 2 3 4 5 6 7 8 | try { Class clasis = Class.forName("런타임 시 결정되는 클래스 이름"); Object obj = new clasis.newInstance(); } catch (ClassNotFoundException e) { } catch (InstantiationException e) { } catch (IllegalAccessException e) { } | cs |
newInstance() 메소드는 기본 생성자를 호출해서 객체를 생성하기 때문에 반드시 클래스에 기본 생성자가 존재해야 합니다. 만약 매개 변수가 있는 생성자를 호출하고 싶다면 리플렉션으로 Constructor 객체를 얻어 newInstance() 메소드를 호출하면 됩니다. newInstance() 메소드는 두 가지 예외가 발생할 수 있는데, InstantiationException 예외는 해당 클래스가 추상 클래스이거나 인터페이스일 경우에 발생하고, IllegalAccessException 예외는 클래스나 생성자가 접근 제한자로 인해 접근할 수 없는 경우에 발생합니다.
newInstance() 메소드의 리턴 타입은 Object 이므로 이것을 원래 클래스 타입으로 변환해야만 메소드를 사용할 수 있습니다. 그렇게 하기 위해서는 강제 타입 변환을 해야 하는데, 클래스 타입을 모르는 상태이므로 변환을 할 수가 없습니다. 이 문제를 해결하기 위해 인터페이스 사용이 필요합니다. 예를 들어 Action 인터페이스와 구현 클래스인 SendAction, ReceiveAction 이 있다고 가정해 봅시다.
Class.forName() 메소드의 파라미터로 "SendAction" 또는 "ReceiveAction"을 주면 Class 객체가 만들어지고, Class 객체의 newInstance() 메소드로 Object 객체를 얻을 수 있습니다. 얻어진 객체는 모두 Action 인터페이스를 구현하고 있기 때문에 다음과 같이 Action 인터페이스 타입으로 변환이 가능합니다. 그런 다음, Action 인터페이스의 execute() 메소드를 호출하면, 개별 클래스의 실체 메소드인 execute() 메소드가 실행됩니다.
1 2 3 4 | class clasis = Class.forName("SendAction"); Action action = (Action) clasis.newInstance(); action.execute(); | cs |
Class.forName() 메소드의 파라미터로 "SendAction"을 줘서 SendAction의 execute가 호출됩니다. 다음 예제를 작성해서 직접 확인해 봅시다.
* Action.java
1 2 3 4 5 6 7 8 9 | package api; public interface Action { public void execute(); } | cs |
* SendAction.java
1 2 3 4 5 6 7 8 9 10 11 | package api; public class SendAction implements Action{ public void execute() { System.out.println("데이터 전송"); } } | cs |
* NewInstanceExam.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | package api; public class NewInstanceExam { public static void main(String[] args) { try { Class<?> clasis = Class.forName("api.SendAction"); Action action = (Action) clasis.newInstance(); action.execute(); } catch (Exception e) { e.printStackTrace(); } } } | cs |
출처 : http://palpit.tistory.com/875
'프로그래밍 > JAVA' 카테고리의 다른 글
Implementation (impl) 을 만들때 @Override 를 붙여야하는 이유 (0) | 2016.09.23 |
---|---|
Forward와 Include의 차이점 (0) | 2016.09.23 |
Spring - Ioc & DI (0) | 2016.09.23 |
StringUtils (0) | 2016.09.23 |
Java Collection Framework (JCF) (0) | 2016.09.23 |