JAVA의 가장 기본이 되는 내용

Class 클래스

cwchoiit 2024. 4. 3. 08:47
728x90
반응형
SMALL

자바에서는 Class 클래스가 있고 이 클래스는 특정 클래스의 정보(메타데이터)를 다루는데 사용된다. Class 클래스를 통해 개발자는 실행중인 자바 애플리케이션 내에서 필요한 클래스의 속성과 메서드에 대한 정보를 조회하고 조작할 수 있다.

 

Class 클래스의 주요 기능은 다음과 같다.

  • 타입 정보 얻기: 클래스의 이름, 슈퍼클래스, 인터페이스, 접근 제한자 등과 같은 정보를 조회할 수 있다.
  • 리플렉션: 클래스에 정의된 메서드, 필드, 생성자 등을 조회하고 이들을 통해 객체 인스턴스를 생성하거나 메서드를 호출하는 등의 작업을 할 수 있다.
  • 동적 로딩과 생성: Class.forName() 메서드를 사용하여 클래스를 동적으로 로드하고, newInstance() 메서드를 통해 새로운 인스턴스를 생성할 수 있다.
  • 애노테이션 처리: 클래스에 적용된 애노테이션을 조회하고 처리하는 기능을 제공한다.

 

특정 클래스 객체를 가져오는 방법은 크게 3가지가 있다.

Class<String> stringClass = String.class;
Class<? extends String> stringClass = new String().getClass();
Class<?> stringClass = Class.forName("java.lang.String");

 

이런 방법들 중 하나를 택해서 특정 클래스의 클래스 객체를 가져오면 다음과 같은 작업들을 해 볼 수 있다.

// 모든 필드 조회
Field[] declaredFields = stringClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
    System.out.println("declaredField = " + declaredField);
}
// 모든 메서드 조회
Method[] declaredMethods = stringClass.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
    System.out.println("declaredMethod = " + declaredMethod);
}
// 부모 클래스 조회
Class<? super String> superclass = stringClass.getSuperclass();
System.out.println("superclass = " + superclass);
// 모든 인터페이스 조회
Class<?>[] interfaces = stringClass.getInterfaces();
for (Class<?> anInterface : interfaces) {
    System.out.println("anInterface = " + anInterface);
}

 

실행해보면 다음과 같이 메타데이터들이 나온다.

declaredField = private final byte[] java.lang.String.value
declaredField = private final byte java.lang.String.coder
declaredField = private int java.lang.String.hash
declaredField = private boolean java.lang.String.hashIsZero
declaredField = private static final long java.lang.String.serialVersionUID
declaredField = static final boolean java.lang.String.COMPACT_STRINGS
declaredField = private static final java.io.ObjectStreamField[] java.lang.String.serialPersistentFields
declaredField = private static final char java.lang.String.REPL
declaredField = public static final java.util.Comparator java.lang.String.CASE_INSENSITIVE_ORDER
declaredField = static final byte java.lang.String.LATIN1
declaredField = static final byte java.lang.String.UTF16
declaredMethod = byte[] java.lang.String.value()
declaredMethod = public boolean java.lang.String.equals(java.lang.Object)
declaredMethod = public int java.lang.String.length()
declaredMethod = public java.lang.String java.lang.String.toString()
declaredMethod = static void java.lang.String.checkIndex(int,int)
declaredMethod = public int java.lang.String.hashCode()
declaredMethod = public void java.lang.String.getChars(int,int,char[],int)
declaredMethod = public int java.lang.String.compareTo(java.lang.Object)
declaredMethod = public int java.lang.String.compareTo(java.lang.String)
declaredMethod = public int java.lang.String.indexOf(java.lang.String,int,int)
declaredMethod = static int java.lang.String.indexOf(byte[],byte,int,java.lang.String,int)
declaredMethod = public int java.lang.String.indexOf(java.lang.String,int)
...
declaredMethod = public java.lang.Object java.lang.String.transform(java.util.function.Function)
declaredMethod = public java.lang.String java.lang.String.formatted(java.lang.Object[])
declaredMethod = public static java.lang.String java.lang.String.copyValueOf(char[],int,int)
declaredMethod = public static java.lang.String java.lang.String.copyValueOf(char[])
declaredMethod = public native java.lang.String java.lang.String.intern()
declaredMethod = static void java.lang.String.checkOffset(int,int)
declaredMethod = static java.lang.String java.lang.String.valueOfCodePoint(int)
declaredMethod = public java.util.Optional java.lang.String.describeConstable()
declaredMethod = private static java.lang.String java.lang.String.lambda$stripIndent$3(int,java.lang.String)
declaredMethod = private static java.lang.String java.lang.String.lambda$indent$2(int,java.lang.String)
declaredMethod = private static java.lang.String java.lang.String.lambda$indent$1(java.lang.String)
declaredMethod = private static java.lang.String java.lang.String.lambda$indent$0(java.lang.String,java.lang.String)
superclass = class java.lang.Object
anInterface = interface java.io.Serializable
anInterface = interface java.lang.Comparable
anInterface = interface java.lang.CharSequence
anInterface = interface java.lang.constant.Constable
anInterface = interface java.lang.constant.ConstantDesc

 

이번엔 직접 만든 클래스로 Class 클래스를 사용해보자.

Hello

public class Hello {
    private final String value;

    public Hello(String value) {
        this.value = value;
    }

    public void hello() {
        System.out.println(value);
    }
}

 

Main

public class Main {
    public static void main(String[] args) throws ClassNotFoundException {

        Class<Hello> helloClass = Hello.class;

        Field[] declaredFields = helloClass.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            System.out.println("declaredField = " + declaredField);
        }
    }
}

 

실행결과:

declaredField = private final java.lang.String clazz.Hello.value

이렇게 기존 방법대로 하는것도 있고, 다음과 같이 문자열로 패키지명과 클래스이름을 통해 클래스를 가져와서 newInstance()로 새 인스턴스를 만드는 방법도 있다.

public class Main {
    public static void main(String[] args) throws Exception {

        Class<?> aClass = Class.forName("clazz.Hello");
        Hello o = (Hello) aClass.getDeclaredConstructor(String.class).newInstance("Hi");

        o.hello();
    }
}

 

실행결과:

Hi

 

newInstance()를 사용하면 반환 타입은 Object 타입이다. 그래서 내가 원하는 클래스로 다운캐스팅 해줘야 한다.

getDeclaredConstructor() 메서드를 사용해서 생성자 메서드를 가져오는데 Hello 클래스는 파라미터가 있는 생성자밖에 없기 때문에 파라미터로 파라미터 타입이 String인 생성자를 가져와서 newInstance() 메서드를 실행한다. 

 

그리고 이렇게 Class 클래스를 이용해서 클래스의 메타 정보를 기반으로 클래스에 정의된 메서드, 필드, 생성자 등을 조회하고, 이들을 통해 객체 인스턴스를 생성하거나 메서드를 호추랗는 작업을 할 수 있는데 이를 리플렉션이라고 한다.

728x90
반응형
LIST

'JAVA의 가장 기본이 되는 내용' 카테고리의 다른 글

날짜와 시간  (0) 2024.04.04
Enum  (0) 2024.04.03
Wrapper Class  (0) 2024.04.02
Method Chaining  (0) 2024.04.02
String 클래스  (0) 2024.04.01