Class.isAssignableFrom()

2025. 6. 3. 16:43High 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 타입 변수에 담을 수 있는가?"

 

📚 참고 링크

 

🔖 참고 사항

  • instanceof vs isInstance() vs isAssignableFrom() 비교
  • ✅ 인터페이스 다중 구현 시의 동작 분석
  • ✅ 제네릭 타입을 동반한 활용 예제 (Class<T> 기반 유틸리티)