2025. 6. 3. 16:43ㆍHigh Level Programming Language/Reflection
🧠 Java Class.isAssignableFrom()
– 클래스 간 타입 대입 가능성을 런타임에 검증하는 핵심 리플렉션 메서드
A(Class 객체)는 B(아규먼트)로부터 할당 가능하다
→ B 타입의 객체를 A 타입 변수에 대입할 수 있다는 의미
A a = new B(); // ✅ A는 B로부터 할당 가능하다
✅ 개요
Class.isAssignableFrom(Class<?> cls)
는 Java 리플렉션 API의 중요한 메서드 중 하나로,
클래스 간 타입 계층 관계를 런타임에 안전하게 검사할 수 있게 해줍니다.
이 메서드는 특히 다음과 같은 상황에서 자주 사용됩니다:
- 프레임워크에서 특정 타입을 구현한 클래스를 찾을 때
- DI(의존성 주입) 컨테이너에서 인터페이스 구현체를 매핑할 때
- 직렬화/역직렬화 시 타입 체크
- 플러그인 시스템 구현 시 타입 제한 조건 체크
📘 메서드 시그니처
public boolean isAssignableFrom(Class<?> cls)
🔑 의미
이 Class
객체가 나타내는 타입이,
파라미터로 주어진 cls
타입의 슈퍼 클래스이거나 슈퍼 인터페이스이면 true
.
즉, 다음과 같은 의미입니다:
T1.class.isAssignableFrom(T2.class)
→ "T2
타입 객체를 T1
타입 변수에 담을 수 있는가?"
🔍 자바 언어 명세(JLS) 기반 해석
Java 언어 명세에서는 이를 다음 두 가지 변환으로 분류합니다:
1. 📎 Identity Conversion (동일 변환) - JLS §5.1.1
A.class.isAssignableFrom(A.class)
는 항상true
2. 📎 Widening Reference Conversion (확대 참조 변환) - JLS §5.1.4
- 서브타입 → 슈퍼타입으로 자동 변환이 가능한 경우
- 예:
Integer → Number
,HashMap → Map
,ArrayList → List
하지만 기본형(primitive type)인 경우에는 아래처럼 **정확히 동일할 때만 true
**입니다:
int.class.isAssignableFrom(int.class); // true
int.class.isAssignableFrom(long.class); // false ❌
✅ 실전 예제: 상속과 인터페이스
📦 클래스와 인터페이스 선언
interface Animal {}
class Mammal implements Animal {}
class Dog extends Mammal {}
class Cat extends Mammal {}
🧪 다양한 타입 비교 테스트
public class AssignableFromDemo {
public static void main(String[] args) {
System.out.println(Mammal.class.isAssignableFrom(Dog.class)); // true
System.out.println(Animal.class.isAssignableFrom(Dog.class)); // true
System.out.println(Dog.class.isAssignableFrom(Mammal.class)); // false
System.out.println(Dog.class.isAssignableFrom(Dog.class)); // true
// 인터페이스 관계
System.out.println(Animal.class.isAssignableFrom(Cat.class)); // true
// 기본형
System.out.println(int.class.isAssignableFrom(int.class)); // true
System.out.println(int.class.isAssignableFrom(long.class)); // false
}
}
🔍 결과 요약
표현식 | 의미 | 결과 |
---|---|---|
Mammal.class.isAssignableFrom(Dog.class) |
Dog → Mammal 가능? | ✅ true |
Dog.class.isAssignableFrom(Mammal.class) |
Mammal → Dog 가능? | ❌ false |
Animal.class.isAssignableFrom(Dog.class) |
Dog → Animal 가능? | ✅ true |
int.class.isAssignableFrom(long.class) |
long → int 가능? | ❌ false |
🧠 핵심 개념 비교
개념 | 설명 | 예시 |
---|---|---|
instanceof |
객체가 특정 타입인지 확인 | obj instanceof Animal |
isAssignableFrom |
클래스 간 대입 가능성 확인 | Animal.class.isAssignableFrom(Dog.class) |
즉:
instanceof
→ 객체에 대해 사용isAssignableFrom()
→ 클래스 타입에 대해 사용 (주로 리플렉션에서 활용)
✅ 실전 예제: 🎯 특정 클래스가 특정 인터페이스를 구현했는지 확인하는 법
Class.isAssignableFrom()의 대표적인 실전 활용
✅ 문제 상황
어떤 클래스가 특정 인터페이스(예: Runnable, Serializable, MyService)를 구현하고 있는지 리플렉션을 통해 런타임에 확인하고 싶다면 어떻게 할까?
예를 들어 다음과 같은 구조가 있다고 가정합니다.
interface Worker {}
class HumanWorker implements Worker {}
class Robot {}
class PartTimeWorker extends HumanWorker {}
이제 우리는 다음 질문에 YES/NO로 답하고 싶습니다:
클래스 | Worker 인터페이스 구현 여부 |
HumanWorker | ✅ Yes |
Robot | ❌ No |
PartTimeWorker | ✅ Yes (상속 통해) |
✅ 리플렉션으로 구현체 여부 확인
public class InterfaceCheckExample {
public static void main(String[] args) {
checkImplements(Worker.class, HumanWorker.class); // ✅ true
checkImplements(Worker.class, Robot.class); // ❌ false
checkImplements(Worker.class, PartTimeWorker.class); // ✅ true
}
static void checkImplements(Class<?> interfaceType, Class<?> targetClass) {
if (!interfaceType.isInterface()) {
throw new IllegalArgumentException("첫 번째 아규먼트는 인터페이스여야 합니다.");
}
boolean result = interfaceType.isAssignableFrom(targetClass);
System.out.printf("📌 %s implements %s ? → %s%n",
targetClass.getSimpleName(),
interfaceType.getSimpleName(),
result ? "✅ YES" : "❌ NO");
}
}
🧠 핵심 개념 요약
interfaceType.isAssignableFrom(targetClass)
→ targetClass가 해당 인터페이스를 직접 또는 간접 구현(상속 포함) 했는가?
타입 체크 방향 | 의미 | 예시 결과 | |
인터페이스.class.isAssignableFrom(구현체.class) | 구현 여부 검사 | Worker.class.isAssignableFrom(HumanWorker.class) | ✅ |
구현체.class.isAssignableFrom(인터페이스.class) | ❌ 잘못된 방향 | HumanWorker.class.isAssignableFrom(Worker.class) | ❌ |
⚠️ 실수 방지 가이드
실수 유형 | 예시 | 문제점 |
방향 반대 | 구현체.class.isAssignableFrom(인터페이스.class) | 항상 false |
클래스에 isInterface() 확인 안 함 | 런타임 예외 가능 | |
instanceof와 혼동 | obj instanceof 인터페이스는 객체용, isAssignableFrom()은 클래스용 |
🧪 참고: 자바의 내부 동작
- JVM은 .class 파일에 implements 또는 extends 정보를 저장함.
- Class.getInterfaces()를 직접 조회해서 인터페이스를 순회하며 검사할 수도 있음.
- 하지만 isAssignableFrom()은 상속 구조까지 포함해주기 때문에 더 안전하고 간편.
// 완전히 동일한 로직을 수동으로 하면 이렇게 복잡해짐
Class<?> clazz = PartTimeWorker.class;
for (Class<?> iface : clazz.getInterfaces()) {
if (iface == Worker.class) {
...
}
}
✅ 요약
특정 클래스가 특정 인터페이스를 구현했는지를 런타임에 검사하려면
인터페이스.class.isAssignableFrom(타겟클래스.class)를 사용하라!
✔️ 직간접 구현까지 포함해서 정확하게 검사
✔️ 프레임워크/플러그인/DI 컨테이너에서 필수적으로 사용됨
🛠️ 실전 사용 사례
✅ 1. 커스텀 클래스 스캐너
for (Class<?> clazz : allClassesInPackage) {
if (MyService.class.isAssignableFrom(clazz) && !clazz.isInterface()) {
System.out.println("등록 대상 클래스: " + clazz.getName());
}
}
✅ 2. 스프링 DI 컨테이너 초기화
Map<Class<?>, Object> beanMap = new HashMap<>();
for (Class<?> clazz : scannedComponents) {
if (SomeRepository.class.isAssignableFrom(clazz)) {
Object instance = clazz.getDeclaredConstructor().newInstance();
beanMap.put(clazz, instance);
}
}
✅ 3. 어댑터 또는 핸들러 체인 등록
public interface Handler {}
public class LoggingHandler implements Handler {}
public void registerHandler(Class<?> handlerClass) {
if (!Handler.class.isAssignableFrom(handlerClass)) {
throw new IllegalArgumentException("Handler 타입이 아닙니다.");
}
// 안전하게 인스턴스 생성 가능
}
🧪 잘못된 사용 예 – 실수 방지
System.out.println(String.class.isAssignableFrom(Object.class)); // false ❗
Object → String
은 업캐스팅 불가 → false
항상 슈퍼 타입에서 서브 타입을 검사해야 함
✅ 결론
isAssignableFrom()
은 클래스 간 대입 가능성을 판단할 때 사용- 특히 리플렉션 기반 코드, DI 컨테이너, 스캐너, 프레임워크 개발에서 핵심
- **항상 “왼쪽(수신자)이 슈퍼 타입인지?”**를 물어보는 메서드임에 주의
🧠 외우는 문장
A.class.isAssignableFrom(B.class)
→ "B
타입 객체를 A
타입 변수에 담을 수 있는가?"
📚 참고 링크
- Java Language Specification §5.1.1 – Identity Conversion
- Java Language Specification §5.1.4 – Widening Reference Conversion
- Java Reflection API – Class Documentation
🔖 참고 사항
- ✅
instanceof
vsisInstance()
vsisAssignableFrom()
비교 - ✅ 인터페이스 다중 구현 시의 동작 분석
- ✅ 제네릭 타입을 동반한 활용 예제 (
Class<T>
기반 유틸리티)
'High Level Programming Language > Reflection' 카테고리의 다른 글
CGLIB 프록시 호출 흐름 완전 분석 (3) | 2025.06.06 |
---|---|
String 클래스가 구현하는 Comparable<String> 인터페이스 (1) | 2025.06.02 |
The Reflection API (0) | 2025.05.28 |
java.base (0) | 2024.07.09 |
Lesson: Arrays and Enumerated Types (0) | 2024.07.06 |