Try using it in your preferred language.

English

  • English
  • 汉语
  • Español
  • Bahasa Indonesia
  • Português
  • Русский
  • 日本語
  • 한국어
  • Deutsch
  • Français
  • Italiano
  • Türkçe
  • Tiếng Việt
  • ไทย
  • Polski
  • Nederlands
  • हिन्दी
  • Magyar
translation

AI가 번역한 다른 언어 보기

제이온

[Java] Reflection 개념 및 사용 방법

  • 작성 언어: 한국어
  • 기준국가: 모든 국가 country-flag

언어 선택

  • 한국어
  • English
  • 汉语
  • Español
  • Bahasa Indonesia
  • Português
  • Русский
  • 日本語
  • Deutsch
  • Français
  • Italiano
  • Türkçe
  • Tiếng Việt
  • ไทย
  • Polski
  • Nederlands
  • हिन्दी
  • Magyar

durumis AI가 요약한 글

  • Reflection은 런타임 시점에 클래스 정보에 접근하여 클래스를 원하는 대로 조작할 수 있는 API입니다.
  • 리플렉션을 통해 클래스의 인스턴스를 생성하고 필드와 메소드를 접근 제어자와 상관 없이 사용할 수 있으며, 특히 프레임워크와 같이 큰 규모의 개발 단계에서 의존 관계를 동적으로 관리하는 데 유용합니다.
  • 그러나 캡슐화를 저해하고 성능 저하를 유발할 수 있으므로 꼭 필요한 경우에만 사용하는 것이 좋습니다.

Reflection이란?

리플렉션은 힙 영역에 로드된 Class 타입의 객체를 통해, 원하는 클래스의 인스턴스를 생성할 수 있도록 지원하고, 인스턴스의 필드와 메소드를 접근 제어자와 상관 없이 사용할 수 있도록 지원하는 API이다.



여기서 로드된 클래스라고 함은, JVM의 클래스 로더에서 클래스 파일에 대한 로딩을 완료한 후, 해당 클래스의 정보를 담은 Class 타입의 객체를 생성하여 메모리의 힙 영역에 저장해 둔 것을 의미한다. new 키워드를 통해 만드는 객체와는 다른 것임을 유의하자. 만약 해당 Class 타입의 객체에 이해가 부족하다면 java.lang.class 객체의 JDK 문서를 확인하면 좋다.


사용 방법

리플렉션을 사용하기에 앞서, 힙 영역에 로드된 클래스 타입의 객체를 가져와야 한다. 총 3가지 방법이 있다.


  • 클래스.class 로 가져오기
  • 인스턴스.getClass() 로 가져오기
  • Class.forName("클래스명") 으로 가져오기


public class Member {

    private String name;

    protected int age;

    public String hobby;

    public Member() {
    }

    public Member(String name, int age, String hobby) {
        this.name = name;
        this.age = age;
        this.hobby = hobby;
    }

    public void speak(String message) {
        System.out.println(message);
    }

    private void secret() {
        System.out.println("비밀번호는 1234입니다.");
    }

    @Override
    public String toString() {
        return "Member{" +
            "name='" + name + '\'' +
            ", age=" + age +
            ", hobby='" + hobby + '\'' +
            '}';
    }
}

public class Main {

    public static void main(String[] args) throws ClassNotFoundException {
        Class memberClass = Member.class;
        System.out.println(System.identityHashCode(memberClass));

        Member member = new Member("제이온", 23, "다라쓰 개발");
        Class memberClass2 = member.getClass();
        System.out.println(System.identityHashCode(memberClass2));

        Class memberClass3 = Class.forName("{패키지명}.Member");
        System.out.println(System.identityHashCode(memberClass3));
    }
}

// 실행 결과
1740000325
1740000325
1740000325

3가지 방법으로 가져온 Class 타입의 인스턴스는 모두 같은 것을 확인할 수 있다. 어떤 방식을 사용하든 해시 값이 같으므로 상황에 따라 적절하게 사용하면 된다.


이제, 가져 온 Class 타입을 통해 해당 클래스의 인스턴스를 생성할 수도 있고, 인스턴스의 필드와 메소드를 접근 제어자와 상관 없이 사용할 수 있게 되었다. 우선 해당 클래스의 인스턴스를 생성해 보자.


public class Main {

    public static void main(String[] args) throws Exception {
        // Member의 모든 생성자 출력
        Member member = new Member();
        Class memberClass = member.getClass();
        Arrays.stream(memberClass.getConstructors()).forEach(System.out::println);

        // Member의 기본 생성자를 통한 인스턴스 생성
        Constructor constructor = memberClass.getConstructor();
        Member member2 = constructor.newInstance();
        System.out.println("member2 = " + member2);

        // Member의 다른 생성자를 통한 인스턴스 생성
        Constructor fullConstructor =
            memberClass.getConstructor(String.class, int.class, String.class);
        Member member3 = fullConstructor.newInstance("제이온", 23, "다라쓰 개발");
        System.out.println("member3 = " + member3);
    }
}

// 실행 결과
public Member()
public Member(java.lang.String,int,java.lang.String)
member2 = Member{name='null', age=0, hobby='null'}
member3 = Member{name='제이온', age=23, hobby='다라쓰 개발'}

getConstructor() 를 통해 생성자를 얻어 오고, newInstance() 를 통해 Member 인스턴스를 동적으로 생성해 줄 수 있다.

마지막으로 인스턴스의 필드와 메소드를 접근 제어자와 상관 없이 접근하여 사용해 보자.

public class Main {

    public static void main(String[] args) throws Exception {
        Member member = new Member("제이온", 23, "다라쓰 개발");
        Class memberClass = member.getClass();

        // 필드 접근
        Field[] fields = memberClass.getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            System.out.println(field.get(member));
        }
        fields[0].set(member, "제이온2");
        System.out.println(member);

        // 메소드 접근
        Method speakMethod = memberClass.getDeclaredMethod("speak", String.class);
        speakMethod.invoke(member, "리플렉션 테스트");

        Method secretMethod = memberClass.getDeclaredMethod("secret");
        secretMethod.setAccessible(true);
        secretMethod.invoke(member);
    }
}

// 실행 결과
제이온
23
다라쓰 개발
Member{name='제이온2', age=23, hobby='다라쓰 개발'}
리플렉션 테스트
비밀번호는 1234입니다.

getDeclaredFileds() 를 통해 클래스의 인스턴스 변수를 모두 가져올 수 있고, get() 을 통해 필드 값을 반환받을 수 있고, set() 을 통해 필드 값을 수정할 수 있는 것을 알 수 있다. 이때 주의할 점은 private 접근 제어자가 있는 필드에 접근할 때는 setAccessible() 의 인자를 true로 넘겨주어야 한다.


메소드도 getDeclaredMethod() 를 통해 메소드를 가져올 수 있다. 이때 메소드의 이름과 파라미터의 타입을 같이 인자로 넘겨줘야 한다. 마찬가지로 private 접근 제어자가 있는 메소드에 접근할 때는 setAccessible() 의 인자를 true로 설정해야 한다. 마지막으로 invoke() 메소드를 통해 리플렉션 API로 얻어 온 메소드를 호출할 수 있다.


장단점

  • 장점
    • 런타임 시점에서 클래스의 인스턴스를 생성하고, 접근 제어자와 관계 없이 필드와 메소드에 접근하여 필요한 작업을 수행할 수 있는 유연성을 가지고 있다.
  • 단점
    • 캡슐화를 저해한다.
    • 런타임 시점에서 인스턴스를 생성하므로 컴파일 시점에서 해당 타입을 체크할 수 없다.
    • 런타임 시점에서 인스턴스를 생성하므로 구체적인 동작 흐름을 파악하기 어렵다.
    • 단순히 필드 및 메소드를 접근할 때보다 리플렉션을 사용하여 접근할 때 성능이 느리다. (모든 상황에서 성능이 느리지는 않음.)


사용하는 이유

리플렉션 API를 통해 런타임 중, 클래스 정보에 접근하여 클래스를 원하는 대로 조작할 수 있다. 심지어 private 접근 제어자로 선언한 필드나 메소드까지 조작이 가능하다. 객체 지향 설계에서 중요한 캡슐화가 깨지므로 사용하면 안 될 기술처럼 보인다.


규모가 작은 콘솔 단계에서는 개발자가 충분히 컴파일 시점에 프로그램에서 사용될 객체와 의존 관계를 모두 파악할 수 있다. 하지만 프레임워크와 같이 큰 규모의 개발 단계에서는 수많은 객체와 의존 관계를 파악하기 어렵다. 이때 리플렉션을 사용하면 동적으로 클래스를 만들어서 의존 관계를 맺어줄 수 있다.


가령, Spring의 Bean Factory를 보면, @Controller, @Service, @Repository 등의 어노테이션만 붙이면 Bean Factory에서 알아서 해당 어노테이션이 붙은 클래스를 생성하고 관리해 주는 것을 알 수 있다. 개발자는 Bean Factory에 해당 클래스를 알려준 적이 없는데, 이것이 가능한 이유는 바로 리플렉션 덕분이다. 런타임에 해당 어노테이션이 붙은 클래스를 탐색하고 발견한다면, 리플렉션을 통해 해당 클래스의 인스턴스를 생성하고 필요한 필드를 주입하여 Bean Factory에 저장하는 식으로 사용이 된다.


물론, 위에서 말했듯 캡슐화를 저해하므로 꼭 필요한 상황에서만 사용하는 것이 좋다.


출처

제이온
제이온
제이온
제이온
[이펙티브 자바] 아이템 1. 생성자 대신 정적 팩터리 메서드를 고려하라 정적 팩터리 메서드는 생성자 대신 인스턴스를 생성하는 데 사용할 수 있는 유용한 방법입니다. 이름을 가질 수 있고, 생성자보다 더 많은 유연성을 제공하며, 플라이웨이트 패턴, 싱글톤 패턴, 서비스 제공자 프레임워크와 같은 디자인 패턴을 구현하는 데 사용할 수 있습니다.

2024년 4월 27일

[이펙티브 자바] 아이템 4. 인스턴스화를 막으려거든 private 생성자를 사용하라 정적 메서드와 필드만을 가진 유틸리티 클래스는 인스턴스화를 막기 위해 생성자를 private으로 선언하는 것이 좋습니다. 이를 통해 컴파일러가 자동 생성하는 기본 생성자를 제어하고 클래스 외부에서의 인스턴스화를 방지할 수 있습니다.

2024년 4월 28일

[이펙티브 자바] 아이템 6. 불필요한 객체 생성을 피하라 자바에서 불필요한 객체 생성을 최소화하여 성능을 향상시키는 방법에 대해 설명합니다. 문자열, 부울, 정규 표현식, 뷰 객체, 오토 박싱 등 다양한 예시와 함께 객체 재사용의 중요성을 강조합니다. 특히 방어적 복사가 필요한 경우 객체 재사용으로 인한 문제점을 주의해야 합니다.

2024년 4월 28일

[Javascript] Object의 구조 (V8) JavaScript에서 Object는 내부적으로 해시테이블과 유사한 방식으로 동작하지만, V8 엔진에서는 Hidden class를 이용하여 Fast 모드와 Dictionary 모드로 변환되어 성능을 최적화합니다. Hidden class는 객체의 구조를 정의하고 Fast 모드에서는 빠른 속도를 제공하지만, 키 추가 등의 변화가 발생하면 Dictionary 모드로 전환되어 해시테이블처럼 동작하며 성능이 저하될 수 있습니다.
곽경직
곽경직
곽경직
곽경직
곽경직

2024년 3월 18일

개념적 데이터 모델링 개념적 데이터 모델링은 정보를 엔티티로 분리하고, 엔티티 간의 관계를 ERD로 표현하는 과정입니다. 엔티티는 독립적인 데이터 저장 단위이며, 속성은 엔티티가 가진 데이터를 의미합니다. ERD에서는 식별자를 사용하여 엔티티를 고유하게 식별하며, 식별자는 기본 키, 후보 키, 대체 키, 중복 키 등으로 구분됩니다. 엔티티 간의 관계는 존재에 의한 관계와 행위에 의한 관계로 나뉘며, 카디널리티와 옵셔널리티를 통해 수적 관계와 필수/선택적 관계를 표현합니다.
제이의 블로그
제이의 블로그
제이의 블로그
제이의 블로그

2024년 4월 8일

[비전공, 개발자로 살아남기] 14. 신입 개발자 자주 묻는 기술면접 내용 요약 신입 개발자 면접에서 자주 나오는 기술 질문과 답변을 정리했습니다. 메모리 영역, 자료구조, 데이터베이스, 프로그래밍 패러다임, 페이지 교체 알고리즘, 프로세스와 스레드, OSI 7 계층, TCP와 UDP 등 다양한 주제를 다룹니다.
투잡뛰는 개발 노동자
투잡뛰는 개발 노동자
투잡뛰는 개발 노동자
투잡뛰는 개발 노동자

2024년 4월 3일

Flitter 1.0.0 출시: D3를 대체할 svg 라이브러리 Flitter는 웹 개발자가 차트와 다이어그램을 쉽고 빠르게 구현할 수 있도록 돕는 데이터 시각화 라이브러리입니다. 선언적 코드 작성 방식과 고급 레이아웃 계산 기능을 통해 복잡한 데이터 시각화 작업을 간소화합니다.
Meursyphus
Meursyphus
Meursyphus
Meursyphus
Meursyphus

2024년 5월 1일

논리적 데이터 모델링 논리적 데이터 모델링은 개념적 데이터 모델링의 ERD를 관계형 데이터베이스 패러다임에 맞춰 변환하는 기계적인 과정입니다. 맵핑 룰을 기준으로 사각형 엔티티는 테이블로, 원형 속성은 컬럼으로, 관계는 PK 또는 FK로 변환됩니다. 1:1, 1:N, N:M 관계를 처리하는 방법과 제 1, 2, 3 정규화를 통해 테이블을 정제하는 과정을 설명합니다.
제이의 블로그
제이의 블로그
제이의 블로그
제이의 블로그
제이의 블로그

2024년 4월 9일

Rust 입문 가이드 러스트 프로그래밍 입문 가이드: 러스트 설치부터 기본 문법, 프로젝트 관리, 테스트, 최적화, 서드파티 라이브러리 활용까지 다루는 야매 가이드입니다.
곽경직
곽경직
곽경직
곽경직
곽경직

2024년 3월 30일