본문 바로가기

프로그래밍 언어/JAVA

JAVA 입문 - Class 클래스

지난 글: [프로그래밍 언어/JAVA] - JAVA 입문 - wrapper 클래스

 

자바의 모든 클래스와 인터페이스는 컴파일 되고 나면 class 파일로 생성된다. 예로 a.java 파일이 컴파일 되면 a.class 파일이 생성되고 이 class 파일에 클래스나 인터페이스에 대한 변수, 메서드, 생성자 등의 정보가 들어있다. Class 클래스는 컴파일 된 class 파일에 저장된 클래스나 인터페이스 정보를 가져오는 데 사용한다.

 

Class 클래스란?

지금까지는 변수를 선언할 때 자료형을 미리 파악하고 그 자료형에 따라 변수를 선언했다. 그리고 클래스를 사용할 때도 이미 그 클래스 정보(변수, 메서드 등)를 알고 있는 상황에서 프로그램을 만들었다. 하지만 어떤 경우에는 여러 클래스 중에 상황에 따라 다른 클래스를 사용해야 할 때도 있고, 반환받는 클래스가 정확히 어떤 자료형인지 모를 때도 있다 한다. 이렇게 모르는 클래스의 정보를 사용할 경우 우리가 직접 클래스 정보를 찾아야 한다. 이때 Class 클래스를 활용한다.

 

Class 클래스를 선언하고 클래스 정보를 가져오는 방법은 아래처럼 세 가지가 있다.

1.Object 클래스의 getClass( ) 메서드 사용하기

String s = new String( );
Class c = s.getClass( ); //getClass( ) 메서드의 반환형은 Class

2.클래스 파일 이름을 Class 변수에 직접 대입하기

Class c = String.Class;

3.Class.forName("클래스 이름") 메서드 사용하기

Class c = Class.forName("java.lang.String");

 

1번의 경우 Object에 선언한 getClass( ) 메서드는 모든 클래스가 사용할 수 있는 메서드이다. 이 메서드를 사용하려면 이미 생성된 인스턴스가 있어야 한다. 2, 3번의 경우 컴파일된 클래스 파일이 있다면 클래스 이름만으로 Class 클래스를 반환받는다.

 

아래 예제를 보며 Class 클래스를 반환받고 활용해 보자. 우선 테스트에 사용할 Person 클래스를 생성한다.

Person 클래스는 생성자가 3개이고 각 멤버 변수에 get( ), set( ) 메서드를 제공한다. 이를 컴파일하여 Person.class 파일을 생성한다. 이제 Person의 Class 클래스를 가져와보자.

출력 결과

12행의 forName( ) 메서드를 살펴보면 클래스 이름(패키지 이름 포함)으로 가져오는 경우에는 매개변수로 쓰이는 값이 문자열이다. 이때 매개변수로 받은 문자열에 해당하는 클래스가 존재하지 않으면 클래스를 가져오는 데 실패한다. 이때 ClassNotFoundException이 발생한다. 6, 9, 12행에서 Class를 가져온 후 getName( ) 메서드를 호출하면 클래스 이름인 classex.Person이 출력되는 것을 볼 수 있다. 즉 Class 클래스를 통하여 클래스 정보를 알 수 있다.

 

Class 클래스를 활용해 클래스 정보 알아보기

프로그래밍을 하다 보면 내가 사용할 클래스의 자료형을 모르는 경우가 있을 수 있다. 예로 내 컴퓨터에 저장되어 있지 않은 객체를 메모리에 로드하고 생성하는 경우 그 객체의 정보를 알 수 없다. 이때 Class 클래스를 가져올 수 있다면 해당 클래스 정보, 즉 생성자, 메서드, 멤버 변수 정보를 찾을 수 있다. 이렇게 사용하려는 클래스의 자료형을 모르는 상태에서 Class 클래스를 활용하여 그 클래스의 정보를 가져오고, 이 정보를 활용하여 인스턴스를 생성하거나 메서드를 호출하는 방식을 '리플렉션(reflection)'이라고 한다. 

 

Class 클래스의 몇 가지 메서드를 활용하는 예제를 통해 정보를 어떻게 찾는지 알아보자. 아래 예제에서 사용하는 Constructor, Method, Field 등의 클래스는 java.lang.reflect 패키지에 정의되어 있다. Class 클래스와 java.lang.reflect 패키지의 클래스를 사용하면 리플렉션 프로그래밍을 할 수 있다. 아래 예제에서 String 클래스 정보를 가져오는 방법을 살펴보자.

출력 결과

Class 클래스를 가져오기 위해 fonName( ) 메서드를 사용했다. 이 메서드는 정적 메서드이므로 클래스를 생성하지 않아도 사용할 수 있다. 9행에서 String 클래스 이름 java.lang.String을 사용하여 Class 클래스를 가져왔다. 이제 String 클래스의 정보를 알 수 있다. 이처럼 Class 클래스와 java.lang.reflect 패키지에 있는 클래스를 활용하면 클래스 이름만 알아도 클래스의 생성자, 메서드 등의 정보를 알 수 있다. 또한 아직 배우지 않았지만 생성자나 메서드를 직접 호출할 수도 있다고 한다.

 

newInstance( )를 사용해 클래스 생성하기

지금까지 Class 클래스를 사용해 클래스의 정보만을 확인했다. 그럼 이 정보를 바탕으로 인스턴스도 생성할 수 있을까? Class 클래스의 메서드 중 newInstance( ) 메서드를 사용하면 된다. newInstance( ) 메서드는 항상 Object형을 반환하므로 생성된 객체형으로 형 변환해야 한다. 앞에서 만든 Person 클래스의 인스턴스를 Class 클래스와 newInstance( ) 메서드를 사용해 생성해보자.

출력 결과

코드를 살펴보면 두 가지 방식으로 인스턴스를 생성했다. 첫 번째는 기존에 배운대로 Person 클래스의 변수를 선언하고 생성자를 사용하여 생성하는 방식이다. 두 번째는 오늘 배운 Class 클래스의 newInstance( ) 메서드를 사용해 인스턴스를 생성했다. 9행에서 Person 클래스 이름을 사용하여 Class 클래스를 반환했다. 그리고 Class 클래스의 newInstance( ) 메서드를 호출하면 Person 클래스의 디폴트 생성자가 호출되어 인스턴스가 생성된다. newInstance( )의 반환 값이 Object이므로 Person 클래스로 다운 캐스팅한 것을 볼 수 있다. 출력 결과를 보면 직접 생성자를 호출한 경우와 newInstance( )로 생성한 경우 모두 인스턴스가 잘 생성되었다.

 

Class 클래스를 사용하는 방법은 클래스의 자료형을 직접 사용하여 프로그래밍하는 것보다 더 복잡하고, 예외 처리도 해야 한다. 이미 우리가 자료형을 알고 있는 클래스인 경우 또는 컴파일할 때 직접 참조할 수 있는 클래스는 Class 클래스를 활용할 필요가 없다. 클래스의 정보를 모두 알고 있는 상황에서 리플렉션 프로그래밍을 하면 코드가 복잡해지고 속도도 느려지기 때문이다. 하여 리플렉션 프로그래밍은 컴파일 시점에 알 수 없는 클래스, 즉 프로그램 실행 중에 클래스를 메모리에 로딩하거나 객체가 다른 곳에 위치해서 원격으로 로딩하고 생성할 때 사용한다.

 

Class.forName( )을 사용해 동적 로딩하기

대부분의 클래스 정보는 프로그램이 로딩될 때 이미 메모리에 있다. 그런데 이런 경우를 생각해보자. 어떤 회사에서 개발한 시스템이 있는데, 그 시스템은 여러 종류의 데이터 베이스를 지원한다. 오라클, MySQL, MS-SQL 등 여러 데이터베이스를 연동할 수 있다. 그렇다고 이 시스템을 컴파일할 때 모든 데이터베이스 라이브러리(드라이버)를 같이 컴파일할 필요는 없다. 시스템을 구동할 때 어떤 데이터베이스와 연결할지만 결정되면 해당 드라이버만 로딩하면 된다. 회사가 사용하는 데이터베이스 정보는 환경 파일에서 읽어 올 수도 있고 다른 변수 값으로 받을 수도 있다. 즉 프로그램 실행 이후 클래스의 로딩이 필요한 경우 클래스의 '동적 로딩(dynamic loading)' 방식을 사용한다. 자바는 Class.forName( ) 메서드를 동적 로딩으로 제공한다고 한다.

Class pClass = Class.forName("classex.Person");

forName( ) 메서드를 살펴보면 매개변수로 문자열을 입력받는다. 이때 입력받는 문자열을 변수로 선언하여 변수 값만 바꾸면 다른 클래스를 로딩할 수 있다.

String className = "classex.Person";
Class pClass = Class.forName(className);

위처럼 작성하고 className 변수에 다른 문자열을 대입하면 필요에 따라 로딩되는 클래스를 동적으로 변경할 수 있다.

 

forName( ) 메서드 사용시 유의할 점

forName( ) 메서드를 사용하여 Class 클래스를 가져올 때 가장 유의할 점은 해당 forName("클래스 이름")의 클래스 이름이 문자열 값이므로, 문자열에 오류가 있어도 컴파일할 때는 그 오류를 알 수 없다. 결국 프로그램이 실행되고 메서드가 호출될 때 클래스 이름에 해당하는 클래스가 없다면 ClassNotFoundException이 발생한다. 즉 동적 로딩 방식은 컴파일할 때 오류를 알 수 없다. 하지만 위의 설명처럼 여러 클래스 중 하나를 선택한다거나, 시스템 연동 중 매개변수로 넘어온 값에 해당하는 클래스가 로딩되고 실행되는 경우에는 동적 로딩 방식을 유연하게 사용할 수 있다. 동적 로딩을 통해 Class 클래스를 가져올 수 있다면 리플렉션 프로그래밍으로 객체를 생성하고 활용할 수 있다.

 

동적 로딩 방식은 자바만의 것이 아니다. 다른 언어에도 실행 중에 라이브러리를 로딩하는 방식을 제공하고 있다. 

 

참고 서적: 자바 프로그래밍 입문 - 박은종

'프로그래밍 언어 > JAVA' 카테고리의 다른 글